From cc707cf66ad0dae989f40d835d77bc8edea71bdb Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 15 Dec 2021 00:35:48 -0800 Subject: [PATCH 01/22] Batching Engine - Implement alternate backend using SBBs --- .../jozufozu/flywheel/api/struct/Batched.java | 4 +- .../api/struct/BatchingTransformer.java | 10 +- .../jozufozu/flywheel/backend/Backend.java | 24 +- .../backend/instancing/AbstractInstancer.java | 8 + .../flywheel/backend/instancing/Engine.java | 1 + .../backend/instancing/InstanceWorld.java | 22 +- .../instancing/InstancedRenderDispatcher.java | 4 + .../instancing/batching/BatchedMaterial.java | 4 +- .../batching/BatchedMaterialGroup.java | 27 +- .../instancing/batching/BatchingEngine.java | 16 +- .../instancing/batching/CPUInstancer.java | 21 +- .../instancing/batching/FormatContext.java | 8 + .../instancing/InstancingEngine.java | 5 + .../backend/model/DirectBufferBuilder.java | 8 + .../backend/model/DirectVertexConsumer.java | 136 +++++ .../flywheel/backend/model/package-info.java | 6 + .../flywheel/backend/struct/BufferWriter.java | 20 +- .../backend/struct/UnsafeBufferWriter.java | 4 +- .../flywheel/config/EngineConfigCommand.java | 32 ++ .../jozufozu/flywheel/config/FlwCommands.java | 1 + .../jozufozu/flywheel/config/FlwConfig.java | 4 + .../jozufozu/flywheel/config/FlwEngine.java | 57 +++ .../jozufozu/flywheel/config/FlwPackets.java | 6 + .../config/SConfigureEnginePacket.java | 38 ++ .../flywheel/core/materials/BasicData.java | 11 +- .../flywheel/core/materials/FlatLit.java | 2 + .../core/materials/UnsafeBasicWriter.java | 25 + .../materials/model/ModelTransformer.java | 12 - .../core/materials/model/ModelType.java | 10 +- .../materials/model/UnsafeModelWriter.java | 23 + .../model/writer/UnsafeModelWriter.java | 35 -- .../core/materials/oriented/OrientedType.java | 13 +- .../{writer => }/UnsafeOrientedWriter.java | 20 +- .../flywheel/core/materials/package-info.java | 6 + .../flywheel/core/model/BakedModelModel.java | 138 ------ .../flywheel/core/model/BlockModel.java | 18 +- .../jozufozu/flywheel/core/model/Model.java | 3 + .../flywheel/core/model/ModelPart.java | 96 ++++ .../core/model/RecordingVertexConsumer.java | 70 --- .../flywheel/core/model/SuperByteBuffer.java | 468 ++++++++++++++++++ .../flywheel/core/model/VecBufferWriter.java | 6 + .../flywheel/core/model/VertexRecording.java | 15 - .../flywheel/core/model/WorldModel.java | 9 +- .../flywheel/mixin/BufferBuilderMixin.java | 58 +++ .../flywheel/util/BufferBuilderReader.java | 31 +- .../jozufozu/flywheel/util/ModelReader.java | 35 ++ src/main/resources/flywheel.mixins.json | 1 + 47 files changed, 1215 insertions(+), 356 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/batching/FormatContext.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java create mode 100644 src/main/java/com/jozufozu/flywheel/config/FlwEngine.java create mode 100644 src/main/java/com/jozufozu/flywheel/config/SConfigureEnginePacket.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/materials/UnsafeBasicWriter.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/materials/model/ModelTransformer.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/materials/model/UnsafeModelWriter.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/materials/model/writer/UnsafeModelWriter.java rename src/main/java/com/jozufozu/flywheel/core/materials/oriented/{writer => }/UnsafeOrientedWriter.java (57%) create mode 100644 src/main/java/com/jozufozu/flywheel/core/materials/package-info.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/BakedModelModel.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/RecordingVertexConsumer.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/VertexRecording.java create mode 100644 src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java create mode 100644 src/main/java/com/jozufozu/flywheel/util/ModelReader.java diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/Batched.java b/src/main/java/com/jozufozu/flywheel/api/struct/Batched.java index ddf7e17b8..a61e326fd 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/Batched.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/Batched.java @@ -1,10 +1,8 @@ package com.jozufozu.flywheel.api.struct; -import com.jozufozu.flywheel.core.model.Model; - public interface Batched extends StructType { - BatchingTransformer getTransformer(Model model); + BatchingTransformer getTransformer(); @Override default Batched asBatched() { diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java b/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java index 16280a457..4b7e9f5d7 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java @@ -1,11 +1,9 @@ package com.jozufozu.flywheel.api.struct; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; +import com.jozufozu.flywheel.core.model.SuperByteBuffer; -public abstract class BatchingTransformer { +@FunctionalInterface +public interface BatchingTransformer { - public void draw(S s, PoseStack stack, VertexConsumer consumer) { - - } + void transform(S s, SuperByteBuffer b); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 2a14e44ff..ba02ae599 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -17,10 +17,13 @@ import com.jozufozu.flywheel.api.FlywheelWorld; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.config.FlwConfig; +import com.jozufozu.flywheel.config.FlwEngine; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; @@ -29,6 +32,7 @@ public class Backend { public static final Logger log = LogManager.getLogger(Backend.class); protected static final Backend INSTANCE = new Backend(); + private FlwEngine engine; public static Backend getInstance() { return INSTANCE; @@ -57,12 +61,13 @@ public class Backend { * (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use. */ public String getBackendDescriptor() { - if (canUseInstancing()) { - return "GL33 Instanced Arrays"; - } + if (enabled) { + ClientLevel level = Minecraft.getInstance().level; - if (canUseVBOs()) { - return "VBOs"; + if (level == null) { + return "Invalid"; + } + return InstancedRenderDispatcher.getEngineName(level); } return "Disabled"; @@ -134,8 +139,9 @@ public class Backend { instancedArrays = compat.instancedArraysSupported(); - enabled = FlwConfig.get() - .enabled() && !OptifineHandler.usingShaders(); + FlwConfig config = FlwConfig.get(); + enabled = config.enabled() && !OptifineHandler.usingShaders(); + engine = config.client.engine.get(); } public boolean canUseInstancing(@Nullable Level world) { @@ -188,4 +194,8 @@ public class Backend { public static void init() { } + + public FlwEngine getEngine() { + return engine; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index 11aec327e..b6e85dbdc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -58,6 +58,14 @@ public abstract class AbstractInstancer implements Insta anyToRemove = true; } + public int getModelVertexCount() { + return modelData.vertexCount(); + } + + public int numInstances() { + return data.size(); + } + protected BitSet getDirtyBitSet() { final int size = data.size(); final BitSet dirtySet = new BitSet(size); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/Engine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/Engine.java index 1e33c16db..a72a24a01 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/Engine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/Engine.java @@ -3,4 +3,5 @@ package com.jozufozu.flywheel.backend.instancing; import com.jozufozu.flywheel.api.MaterialManager; public interface Engine extends RenderDispatcher, MaterialManager { + String getName(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 7f127021f..f2cd13180 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -2,10 +2,12 @@ package com.jozufozu.flywheel.backend.instancing; import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.ITickableInstance; +import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; +import com.jozufozu.flywheel.config.FlwEngine; import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.event.BeginFrameEvent; @@ -29,12 +31,11 @@ public class InstanceWorld { public InstanceWorld() { - // TODO: finish impl - if (false) { - engine = new BatchingEngine(); - entityInstanceManager = new EntityInstanceManager(engine); - tileEntityInstanceManager = new TileInstanceManager(engine); - } else { + FlwEngine engine = Backend.getInstance() + .getEngine(); + + switch (engine) { + case GL33 -> { InstancingEngine manager = InstancingEngine.builder(Contexts.WORLD) .build(); @@ -43,7 +44,14 @@ public class InstanceWorld { manager.addListener(entityInstanceManager); manager.addListener(tileEntityInstanceManager); - engine = manager; + this.engine = manager; + } + case BATCHING -> { + this.engine = new BatchingEngine(); + entityInstanceManager = new EntityInstanceManager(this.engine); + tileEntityInstanceManager = new TileInstanceManager(this.engine); + } + default -> throw new IllegalArgumentException("Unknown engine type"); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index 426ff035b..c07a1d325 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -41,6 +41,10 @@ public class InstancedRenderDispatcher { getEntities(entity.level).queueUpdate(entity); } + public static String getEngineName(LevelAccessor world) { + return instanceWorlds.get(world).engine.getName(); + } + public static InstanceManager getTiles(LevelAccessor world) { return instanceWorlds.get(world) .getTileEntityInstanceManager(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java index 2593c67bf..ad14cba40 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java @@ -28,9 +28,9 @@ public class BatchedMaterial implements Material { return models.computeIfAbsent(key, $ -> new CPUInstancer<>(type, modelSupplier.get())); } - public void render(PoseStack stack, VertexConsumer buffer) { + public void render(PoseStack stack, VertexConsumer buffer, FormatContext context) { for (CPUInstancer instancer : models.values()) { - instancer.drawAll(stack, buffer); + instancer.drawAll(stack, buffer, context); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java index 7cad92ecb..ea7b65e7c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java @@ -6,6 +6,8 @@ import java.util.Map; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.model.DirectBufferBuilder; +import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -31,8 +33,31 @@ public class BatchedMaterialGroup implements MaterialGroup { public void render(PoseStack stack, MultiBufferSource source) { VertexConsumer buffer = source.getBuffer(state); + if (buffer instanceof DirectBufferBuilder direct) { + DirectVertexConsumer consumer = direct.intoDirectConsumer(calculateNeededVertices()); + + renderInto(stack, consumer, new FormatContext(consumer.hasOverlay())); + + direct.updateAfterWriting(consumer); + } else { + renderInto(stack, buffer, FormatContext.defaultContext()); + } + } + + private int calculateNeededVertices() { + int total = 0; + for (BatchedMaterial material : materials.values()) { + for (CPUInstancer instancer : material.models.values()) { + total += instancer.getModelVertexCount() * instancer.numInstances(); + } + } + + return total; + } + + private void renderInto(PoseStack stack, VertexConsumer consumer, FormatContext context) { for (BatchedMaterial value : materials.values()) { - value.render(stack, buffer); + value.render(stack, consumer, context); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index 27535864a..ba915499e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -8,6 +8,7 @@ import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.event.RenderLayerEvent; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Camera; import net.minecraft.client.renderer.MultiBufferSource; @@ -40,15 +41,28 @@ public class BatchingEngine implements Engine { @Override public void render(RenderLayerEvent event, MultiBufferSource buffers) { + PoseStack stack = event.stack; + + stack.pushPose(); + + stack.translate(-event.camX, -event.camY, -event.camZ); + for (Map.Entry entry : layers.get(event.getLayer()).entrySet()) { BatchedMaterialGroup group = entry.getValue(); - group.render(event.stack, buffers); + group.render(stack, buffers); } + + stack.popPose(); } @Override public void beginFrame(Camera info) { } + + @Override + public String getName() { + return "Batching"; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 955c52a1c..38c5a7510 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -5,18 +5,23 @@ import com.jozufozu.flywheel.api.struct.BatchingTransformer; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.core.model.Model; +import com.jozufozu.flywheel.core.model.SuperByteBuffer; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; public class CPUInstancer extends AbstractInstancer { - private final BatchingTransformer renderer; + private final BatchingTransformer transform; + + private final SuperByteBuffer sbb; public CPUInstancer(StructType type, Model modelData) { super(type, modelData); - renderer = type.asBatched() - .getTransformer(modelData); + sbb = new SuperByteBuffer(modelData); + transform = type.asBatched() + .getTransformer(); } @Override @@ -24,15 +29,19 @@ public class CPUInstancer extends AbstractInstancer { // noop } - public void drawAll(PoseStack stack, VertexConsumer buffer) { - if (renderer == null) { + public void drawAll(PoseStack stack, VertexConsumer buffer, FormatContext context) { + if (transform == null) { return; } renderSetup(); for (D d : data) { - renderer.draw(d, stack, buffer); + if (context.usesOverlay()) sbb.entityMode(); + + transform.transform(d, sbb); + + sbb.renderInto(stack, buffer); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/FormatContext.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/FormatContext.java new file mode 100644 index 000000000..bb0bbd41e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/FormatContext.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.backend.instancing.batching; + +public record FormatContext(boolean usesOverlay) { + + public static FormatContext defaultContext() { + return new FormatContext(false); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 7d1fa1cbe..1332a4a56 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -165,6 +165,11 @@ public class InstancingEngine

implements Engine { } } + @Override + public String getName() { + return "GL33 Instanced Arrays"; + } + @FunctionalInterface public interface OriginShiftListener { void onOriginShift(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java new file mode 100644 index 000000000..b3f08681a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.backend.model; + +public interface DirectBufferBuilder { + + DirectVertexConsumer intoDirectConsumer(int neededVerts); + + void updateAfterWriting(DirectVertexConsumer complete); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java new file mode 100644 index 000000000..0bea53cb4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java @@ -0,0 +1,136 @@ +package com.jozufozu.flywheel.backend.model; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.util.RenderMath; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormatElement; + +public class DirectVertexConsumer implements VertexConsumer { + + public final VertexFormat format; + private final int stride; + public final int startPos; + + private int position = -1; + private int normal = -1; + private int color = -1; + private int uv = -1; + private int uv1 = -1; + private int uv2 = -1; + + private long vertexBase; + private int vertexCount; + + public DirectVertexConsumer(ByteBuffer buffer, VertexFormat format) { + this.format = format; + startPos = buffer.position(); + stride = format.getVertexSize(); + + int offset = 0; + + for (VertexFormatElement element : format.getElements()) { + switch (element.getUsage()) { + case POSITION -> this.position = offset; + case NORMAL -> this.normal = offset; + case COLOR -> this.color = offset; + case UV -> { + switch (element.getIndex()) { + case 0 -> this.uv = offset; + case 1 -> this.uv1 = offset; + case 2 -> this.uv2 = offset; + } + } + } + + offset += element.getByteSize(); + } + + this.vertexBase = MemoryUtil.memAddress(buffer); + } + + public boolean hasOverlay() { + return uv1 >= 0; + } + + @Override + public VertexConsumer vertex(double x, double y, double z) { + if (position < 0) return this; + long base = vertexBase + position; + MemoryUtil.memPutFloat(base, (float) x); + MemoryUtil.memPutFloat(base + 4, (float) y); + MemoryUtil.memPutFloat(base + 8, (float) z); + return this; + } + + @Override + public VertexConsumer color(int r, int g, int b, int a) { + if (color < 0) return this; + long base = vertexBase + color; + MemoryUtil.memPutByte(base, (byte) r); + MemoryUtil.memPutByte(base + 1, (byte) g); + MemoryUtil.memPutByte(base + 2, (byte) b); + MemoryUtil.memPutByte(base + 3, (byte) a); + return this; + } + + @Override + public VertexConsumer uv(float u, float v) { + if (uv < 0) return this; + long base = vertexBase + uv; + MemoryUtil.memPutFloat(base, u); + MemoryUtil.memPutFloat(base + 4, v); + return this; + } + + @Override + public VertexConsumer overlayCoords(int u, int v) { + if (uv1 < 0) return this; + long base = vertexBase + uv1; + MemoryUtil.memPutShort(base, (short) u); + MemoryUtil.memPutShort(base + 2, (short) v); + return this; + } + + @Override + public VertexConsumer uv2(int u, int v) { + if (uv2 < 0) return this; + long base = vertexBase + uv2; + MemoryUtil.memPutShort(base, (short) u); + MemoryUtil.memPutShort(base + 2, (short) v); + return this; + } + + @Override + public VertexConsumer normal(float x, float y, float z) { + if (normal < 0) return this; + long base = vertexBase + normal; + MemoryUtil.memPutByte(base, RenderMath.nb(x)); + MemoryUtil.memPutByte(base + 1, RenderMath.nb(y)); + MemoryUtil.memPutByte(base + 2, RenderMath.nb(z)); + return this; + } + + @Override + public void endVertex() { + vertexBase += stride; + vertexCount++; + } + + @Override + public void defaultColor(int r, int g, int b, int a) { + + } + + @Override + public void unsetDefaultColor() { + + } + + public int getVertexCount() { + return vertexCount; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/model/package-info.java new file mode 100644 index 000000000..3109529e1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/model/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.model; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java index d46cbcaed..94f976f22 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java @@ -18,14 +18,22 @@ public abstract class BufferWriter implements StructWriter { this.stride = this.format.getStride(); } - /** - * Advances the write pointer forward by the stride of one vertex. This should always be called after a - * vertex is written. Implementations which override this should always call invoke the super implementation. - */ - protected void advance() { - + @Override + public final void write(S struct) { + writeInternal(struct); + advance(); } + /** + * Advances the write pointer forward by the stride of one vertex. + * This will always be called after a struct is written, implementors need not call it themselves. + * + * @see #write + */ + protected abstract void advance(); + + protected abstract void writeInternal(S s); + @Override public void seek(int pos) { backingBuffer.position(pos * stride); diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java b/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java index d5e97eeb9..b7c74e7f6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java @@ -15,7 +15,7 @@ import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; */ public abstract class UnsafeBufferWriter extends BufferWriter { /** - * The write pointer into the buffer storage. This is advanced by the vertex stride every time + * The write pointer into the buffer storage. This is advanced by the stride every time * {@link UnsafeBufferWriter#advance()} is called. */ protected long writePointer; @@ -35,8 +35,6 @@ public abstract class UnsafeBufferWriter extends BufferWriter { @Override protected void advance() { this.writePointer += this.stride; - - super.advance(); } private void acquireWritePointer() { diff --git a/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java b/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java new file mode 100644 index 000000000..a5ab057ce --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java @@ -0,0 +1,32 @@ +package com.jozufozu.flywheel.config; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.ArgumentBuilder; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.network.PacketDistributor; +import net.minecraftforge.server.command.EnumArgument; + +public class EngineConfigCommand { + public ArgumentBuilder register() { + return Commands.literal("engine") + .executes(context -> { + ServerPlayer player = context.getSource() + .getPlayerOrException(); + FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket()); + return Command.SINGLE_SUCCESS; + }) + .then(Commands.argument("type", EnumArgument.enumArgument(FlwEngine.class)) + .executes(context -> { + FlwEngine type = context.getArgument("type", FlwEngine.class); + + ServerPlayer player = context.getSource() + .getPlayerOrException(); + FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket(type)); + + return Command.SINGLE_SUCCESS; + })); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 31525410c..3cee5f1eb 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -17,6 +17,7 @@ public class FlwCommands { dispatcher.register(Commands.literal("flywheel") .then(new BooleanConfigCommand("backend", BooleanConfig.ENGINE).register()) .then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register()) + .then(new EngineConfigCommand().register()) ); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java index 0fd5a2673..1efdba072 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java @@ -39,6 +39,7 @@ public class FlwConfig { public static class ClientConfig { public final BooleanValue enabled; + public final ForgeConfigSpec.EnumValue engine; public final BooleanValue debugNormals; public ClientConfig(ForgeConfigSpec.Builder builder) { @@ -46,6 +47,9 @@ public class FlwConfig { enabled = builder.comment("Enable or disable the entire engine") .define("enabled", true); + engine = builder.comment("Enable or disable the entire engine") + .defineEnum("backend", FlwEngine.GL33); + debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal") .define("debugNormals", false); } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java b/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java new file mode 100644 index 000000000..45a09fb45 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java @@ -0,0 +1,57 @@ +package com.jozufozu.flywheel.config; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.OptifineHandler; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; + +public enum FlwEngine { + BATCHING(new TextComponent("Batching").withStyle(ChatFormatting.BLUE)), + GL33(new TextComponent("GL 3.3 Instanced Arrays").withStyle(ChatFormatting.GREEN)), + + ; + + private final Component name; + + FlwEngine(Component name) { + this.name = name; + } + + @Nullable + public static FlwEngine decode(FriendlyByteBuf buffer) { + byte b = buffer.readByte(); + + if (b == -1) return null; + + return values()[b]; + } + + public void encode(FriendlyByteBuf buffer) { + buffer.writeByte(this.ordinal()); + } + + public void switchTo() { + LocalPlayer player = Minecraft.getInstance().player; + if (player == null) return; + +// if (state == BooleanDirective.DISPLAY) { +// Component text = new TextComponent("Flywheel renderer is currently: ").append(boolToText(FlwConfig.get().enabled())); +// player.displayClientMessage(text, false); +// return; +// } + + FlwConfig.get().client.engine.set(this); + + Component text = new TextComponent("Using ").withStyle(ChatFormatting.WHITE).append(name); + + player.displayClientMessage(text, false); + Backend.reloadWorldRenderers(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwPackets.java b/src/main/java/com/jozufozu/flywheel/config/FlwPackets.java index 97c4a0e46..eb968aa52 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwPackets.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwPackets.java @@ -24,5 +24,11 @@ public class FlwPackets { .encoder(SConfigureBooleanPacket::encode) .consumer(SConfigureBooleanPacket::execute) .add(); + + channel.messageBuilder(SConfigureEnginePacket.class, 1, NetworkDirection.PLAY_TO_CLIENT) + .decoder(SConfigureEnginePacket::new) + .encoder(SConfigureEnginePacket::encode) + .consumer(SConfigureEnginePacket::execute) + .add(); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/SConfigureEnginePacket.java b/src/main/java/com/jozufozu/flywheel/config/SConfigureEnginePacket.java new file mode 100644 index 000000000..ac7a4c8e9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/config/SConfigureEnginePacket.java @@ -0,0 +1,38 @@ +package com.jozufozu.flywheel.config; + +import java.util.function.Supplier; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.network.NetworkEvent; + +public class SConfigureEnginePacket { + + private final FlwEngine type; + + public SConfigureEnginePacket() { + type = null; + } + + public SConfigureEnginePacket(FlwEngine type) { + this.type = type; + } + + public SConfigureEnginePacket(FriendlyByteBuf buffer) { + type = FlwEngine.decode(buffer); + } + + public void encode(FriendlyByteBuf buffer) { + if (type != null) + type.encode(buffer); + else + buffer.writeByte(-1); + } + + public void execute(Supplier ctx) { + if (type != null) { + type.switchTo(); + } + ctx.get() + .setPacketHandled(true); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/BasicData.java b/src/main/java/com/jozufozu/flywheel/core/materials/BasicData.java index 1b34a856e..955aba918 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/BasicData.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/BasicData.java @@ -2,6 +2,8 @@ package com.jozufozu.flywheel.core.materials; import com.jozufozu.flywheel.api.InstanceData; +import net.minecraft.client.renderer.LightTexture; + public abstract class BasicData extends InstanceData implements FlatLit { public byte blockLight; @@ -14,18 +16,23 @@ public abstract class BasicData extends InstanceData implements FlatLit> { * @return this */ D setSkyLight(int skyLight); + + int getPackedLight(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/UnsafeBasicWriter.java b/src/main/java/com/jozufozu/flywheel/core/materials/UnsafeBasicWriter.java new file mode 100644 index 000000000..5b36599e0 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/materials/UnsafeBasicWriter.java @@ -0,0 +1,25 @@ +package com.jozufozu.flywheel.core.materials; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; +import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter; + +public abstract class UnsafeBasicWriter extends UnsafeBufferWriter { + + public UnsafeBasicWriter(VecBuffer backingBuffer, StructType vertexType) { + super(backingBuffer, vertexType); + } + + @Override + protected void writeInternal(D d) { + long addr = writePointer; + MemoryUtil.memPutByte(addr, (byte) (d.blockLight << 4)); + MemoryUtil.memPutByte(addr + 1, (byte) (d.skyLight << 4)); + MemoryUtil.memPutByte(addr + 2, d.r); + MemoryUtil.memPutByte(addr + 3, d.g); + MemoryUtil.memPutByte(addr + 4, d.b); + MemoryUtil.memPutByte(addr + 5, d.a); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelTransformer.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelTransformer.java deleted file mode 100644 index 0851fbcc4..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelTransformer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.jozufozu.flywheel.core.materials.model; - -import com.jozufozu.flywheel.api.struct.BatchingTransformer; -import com.jozufozu.flywheel.core.model.Model; - -public class ModelTransformer extends BatchingTransformer { - public ModelTransformer(Model model) { - - - //model.buffer(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java index c815df2f9..9527cbb1f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java @@ -8,8 +8,6 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.Programs; -import com.jozufozu.flywheel.core.materials.model.writer.UnsafeModelWriter; -import com.jozufozu.flywheel.core.model.Model; import net.minecraft.resources.ResourceLocation; @@ -36,7 +34,11 @@ public class ModelType implements Instanced, Batched { } @Override - public BatchingTransformer getTransformer(Model model) { - return null; + public BatchingTransformer getTransformer() { + return (d, b) -> { + b.transform(d.model, d.normal) + .color(d.r, d.g, d.b, d.a) + .light(d.getPackedLight()); + }; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/UnsafeModelWriter.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/UnsafeModelWriter.java new file mode 100644 index 000000000..9d041df58 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/UnsafeModelWriter.java @@ -0,0 +1,23 @@ +package com.jozufozu.flywheel.core.materials.model; + +import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.core.materials.UnsafeBasicWriter; +import com.jozufozu.flywheel.util.WriteUnsafe; + +public class UnsafeModelWriter extends UnsafeBasicWriter { + + public UnsafeModelWriter(VecBuffer backingBuffer, StructType vertexType) { + super(backingBuffer, vertexType); + } + + @Override + protected void writeInternal(ModelData d) { + super.writeInternal(d); + long addr = writePointer + 6; + + ((WriteUnsafe) (Object) d.model).writeUnsafe(addr); + addr += 4 * 16; + ((WriteUnsafe) (Object) d.normal).writeUnsafe(addr); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/writer/UnsafeModelWriter.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/writer/UnsafeModelWriter.java deleted file mode 100644 index 9b04a8534..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/writer/UnsafeModelWriter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.jozufozu.flywheel.core.materials.model.writer; - -import org.lwjgl.system.MemoryUtil; - -import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter; -import com.jozufozu.flywheel.core.materials.model.ModelData; -import com.jozufozu.flywheel.util.WriteUnsafe; - -public class UnsafeModelWriter extends UnsafeBufferWriter { - - public UnsafeModelWriter(VecBuffer backingBuffer, StructType vertexType) { - super(backingBuffer, vertexType); - } - - @Override - public void write(ModelData d) { - long addr = writePointer; - MemoryUtil.memPutByte(addr, d.blockLight); - MemoryUtil.memPutByte(addr + 1, d.skyLight); - MemoryUtil.memPutByte(addr + 2, d.r); - MemoryUtil.memPutByte(addr + 3, d.g); - MemoryUtil.memPutByte(addr + 4, d.b); - MemoryUtil.memPutByte(addr + 5, d.a); - - addr += 6; - - ((WriteUnsafe) (Object) d.model).writeUnsafe(addr); - addr += 4 * 16; - ((WriteUnsafe) (Object) d.normal).writeUnsafe(addr); - - advance(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java index 55f0097f4..c5625bca0 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java @@ -8,8 +8,7 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.Programs; -import com.jozufozu.flywheel.core.materials.oriented.writer.UnsafeOrientedWriter; -import com.jozufozu.flywheel.core.model.Model; +import com.mojang.math.Quaternion; import net.minecraft.resources.ResourceLocation; @@ -36,7 +35,13 @@ public class OrientedType implements Instanced, Batched getTransformer(Model model) { - return null; + public BatchingTransformer getTransformer() { + return (d, sbb) -> { + sbb.light(d.getPackedLight()) + .color(d.r, d.g, d.b, d.a) + .translate(d.posX + d.pivotX, d.posY + d.pivotY, d.posZ + d.pivotZ) + .multiply(new Quaternion(d.qX, d.qY, d.qZ, d.qW)) + .translate(-d.pivotX, -d.pivotY, -d.pivotZ); + }; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/writer/UnsafeOrientedWriter.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/UnsafeOrientedWriter.java similarity index 57% rename from src/main/java/com/jozufozu/flywheel/core/materials/oriented/writer/UnsafeOrientedWriter.java rename to src/main/java/com/jozufozu/flywheel/core/materials/oriented/UnsafeOrientedWriter.java index 3818985e0..c617e6396 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/writer/UnsafeOrientedWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/UnsafeOrientedWriter.java @@ -1,26 +1,20 @@ -package com.jozufozu.flywheel.core.materials.oriented.writer; +package com.jozufozu.flywheel.core.materials.oriented; import org.lwjgl.system.MemoryUtil; -import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter; -import com.jozufozu.flywheel.core.materials.oriented.OrientedData; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.core.materials.UnsafeBasicWriter; -public class UnsafeOrientedWriter extends UnsafeBufferWriter { +public class UnsafeOrientedWriter extends UnsafeBasicWriter { public UnsafeOrientedWriter(VecBuffer backingBuffer, StructType vertexType) { super(backingBuffer, vertexType); } @Override - public void write(OrientedData d) { + protected void writeInternal(OrientedData d) { long addr = writePointer; - MemoryUtil.memPutByte(addr, d.blockLight); - MemoryUtil.memPutByte(addr + 1, d.skyLight); - MemoryUtil.memPutByte(addr + 2, d.r); - MemoryUtil.memPutByte(addr + 3, d.g); - MemoryUtil.memPutByte(addr + 4, d.b); - MemoryUtil.memPutByte(addr + 5, d.a); + super.writeInternal(d); MemoryUtil.memPutFloat(addr + 6, d.posX); MemoryUtil.memPutFloat(addr + 10, d.posY); @@ -32,7 +26,5 @@ public class UnsafeOrientedWriter extends UnsafeBufferWriter { MemoryUtil.memPutFloat(addr + 34, d.qY); MemoryUtil.memPutFloat(addr + 38, d.qZ); MemoryUtil.memPutFloat(addr + 42, d.qW); - - advance(); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/package-info.java b/src/main/java/com/jozufozu/flywheel/core/materials/package-info.java new file mode 100644 index 000000000..09622d998 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/materials/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.core.materials; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BakedModelModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BakedModelModel.java deleted file mode 100644 index a972704fb..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/model/BakedModelModel.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.jozufozu.flywheel.core.model; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -import org.lwjgl.system.MemoryStack; - -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.core.Formats; -import com.jozufozu.flywheel.util.VirtualEmptyModelData; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.math.Vector3f; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.color.item.ItemColors; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.core.Direction; -import net.minecraft.core.Vec3i; - -public class BakedModelModel implements Model { - // DOWN, UP, NORTH, SOUTH, WEST, EAST, null - private static final Direction[] dirs; - - static { - Direction[] directions = Direction.values(); - - dirs = Arrays.copyOf(directions, directions.length + 1); - } - - - public final BakedModel model; - private final int numQuads; - - public BakedModelModel(BakedModel model) { - this.model = model; - - Random random = new Random(); - - int numQuads = 0; - - for (Direction dir : dirs) { - random.setSeed(42); - List quads = model.getQuads(null, dir, random, VirtualEmptyModelData.INSTANCE); - - numQuads += quads.size(); - } - - this.numQuads = numQuads; - } - - @Override - public String name() { - return model.toString(); - } - - @Override - public void buffer(VertexConsumer buffer) { - - Minecraft mc = Minecraft.getInstance(); - - ItemColors itemColors = mc.getItemColors(); - - Random random = new Random(); - - for (Direction dir : dirs) { - random.setSeed(42); - List quads = model.getQuads(null, dir, random, VirtualEmptyModelData.INSTANCE); - - for (BakedQuad bakedQuad : quads) { -// int i = -1; -// if (!itemStack.isEmpty() && bakedQuad.isTinted()) { -// i = itemColors.getColor(itemStack, bakedQuad.getTintIndex()); -// } -// -// byte red = (byte)(i >> 16 & 255); -// byte green = (byte)(i >> 8 & 255); -// byte blue = (byte)(i & 255); - - int[] aint = bakedQuad.getVertices(); - Vec3i faceNormal = bakedQuad.getDirection().getNormal(); - Vector3f normal = new Vector3f((float)faceNormal.getX(), (float)faceNormal.getY(), (float)faceNormal.getZ()); - int intSize = DefaultVertexFormat.BLOCK.getIntegerSize(); - int vertexCount = aint.length / intSize; - - try (MemoryStack memorystack = MemoryStack.stackPush()) { - ByteBuffer bytebuffer = memorystack.malloc(DefaultVertexFormat.BLOCK.getVertexSize()); - IntBuffer intbuffer = bytebuffer.asIntBuffer(); - - for(int j = 0; j < vertexCount; ++j) { - ((Buffer)intbuffer).clear(); - intbuffer.put(aint, j * 8, 8); - float f = bytebuffer.getFloat(0); - float f1 = bytebuffer.getFloat(4); - float f2 = bytebuffer.getFloat(8); -// float cr; -// float cg; -// float cb; -// float ca; -// { -// float r = (float)(bytebuffer.get(12) & 255) / 255.0F; -// float g = (float)(bytebuffer.get(13) & 255) / 255.0F; -// float b = (float)(bytebuffer.get(14) & 255) / 255.0F; -// float a = (float)(bytebuffer.get(15) & 255) / 255.0F; -// cr = r * red; -// cg = g * green; -// cb = b * blue; -// ca = a; -// } - - float u = bytebuffer.getFloat(16); - float v = bytebuffer.getFloat(20); - - buffer.vertex(f, f1, f2); - buffer.normal(normal.x(), normal.y(), normal.z()); - buffer.uv(u, v); - buffer.endVertex(); - } - } - } - } - } - - @Override - public int vertexCount() { - return numQuads * 4; - } - - @Override - public VertexFormat format() { - return Formats.UNLIT_MODEL; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java index 44d81c899..0d3ae3dfb 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java @@ -5,6 +5,7 @@ import java.util.Arrays; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.util.BufferBuilderReader; +import com.jozufozu.flywheel.util.ModelReader; import com.jozufozu.flywheel.util.RenderMath; import com.jozufozu.flywheel.util.VirtualEmptyModelData; import com.mojang.blaze3d.vertex.BufferBuilder; @@ -27,7 +28,7 @@ import net.minecraft.world.level.block.state.BlockState; public class BlockModel implements Model { private static final PoseStack IDENTITY = new PoseStack(); - private final BufferBuilderReader reader; + private final ModelReader reader; private final String name; @@ -69,7 +70,7 @@ public class BlockModel implements Model { for (int i = 0; i < vertexCount; i++) { buffer.vertex(reader.getX(i), reader.getY(i), reader.getZ(i)); - buffer.normal(RenderMath.f(reader.getNX(i)), RenderMath.f(reader.getNY(i)), RenderMath.f(reader.getNZ(i))); + buffer.normal(reader.getNX(i), reader.getNY(i), reader.getNZ(i)); buffer.uv(reader.getU(i), reader.getV(i)); @@ -77,6 +78,11 @@ public class BlockModel implements Model { } } + @Override + public ModelReader getReader() { + return reader; + } + public static BufferBuilder getBufferBuilder(BakedModel model, BlockState referenceState, PoseStack ms) { Minecraft mc = Minecraft.getInstance(); BlockRenderDispatcher dispatcher = mc.getBlockRenderer(); @@ -96,12 +102,4 @@ public class BlockModel implements Model { return builder; } - // DOWN, UP, NORTH, SOUTH, WEST, EAST, null - private static final Direction[] dirs; - - static { - Direction[] directions = Direction.values(); - - dirs = Arrays.copyOf(directions, directions.length + 1); - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Model.java b/src/main/java/com/jozufozu/flywheel/core/model/Model.java index fb8704742..863cef816 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/Model.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/Model.java @@ -3,6 +3,7 @@ package com.jozufozu.flywheel.core.model; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.model.ElementBuffer; import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.util.ModelReader; import com.mojang.blaze3d.vertex.VertexConsumer; /** @@ -76,4 +77,6 @@ public interface Model { default boolean empty() { return vertexCount() == 0; } + + ModelReader getReader(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java index bf4498090..0b87087fb 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java @@ -1,10 +1,15 @@ package com.jozufozu.flywheel.core.model; +import java.nio.ByteBuffer; import java.util.List; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.util.ModelReader; +import com.jozufozu.flywheel.util.RenderMath; import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Vector3f; public class ModelPart implements Model { @@ -48,4 +53,95 @@ public class ModelPart implements Model { public VertexFormat format() { return Formats.UNLIT_MODEL; } + + @Override + public ModelReader getReader() { + return new PartReader(this); + } + + private class PartReader implements ModelReader { + + private final ByteBuffer buffer; + + private PartReader(ModelPart part) { + this.buffer = ByteBuffer.allocate(part.size()); + VecBufferWriter writer = new VecBufferWriter(this.buffer); + + buffer(writer); + } + + private int vertIdx(int vertexIndex) { + return vertexIndex * format().getStride(); + } + + @Override + public float getX(int index) { + return buffer.getFloat(vertIdx(index)); + } + + @Override + public float getY(int index) { + return buffer.getFloat(vertIdx(index) + 4); + } + + @Override + public float getZ(int index) { + return buffer.getFloat(vertIdx(index) + 8); + } + + @Override + public byte getR(int index) { + return (byte) 0xFF; + } + + @Override + public byte getG(int index) { + return (byte) 0xFF; + } + + @Override + public byte getB(int index) { + return (byte) 0xFF; + } + + @Override + public byte getA(int index) { + return (byte) 0xFF; + } + + @Override + public float getU(int index) { + return buffer.getFloat(vertIdx(index) + 15); + } + + @Override + public float getV(int index) { + return buffer.getFloat(vertIdx(index) + 19); + } + + @Override + public int getLight(int index) { + return 0; + } + + @Override + public float getNX(int index) { + return RenderMath.f(buffer.get(vertIdx(index) + 12)); + } + + @Override + public float getNY(int index) { + return RenderMath.f(buffer.get(vertIdx(index) + 13)); + } + + @Override + public float getNZ(int index) { + return RenderMath.f(buffer.get(vertIdx(index) + 14)); + } + + @Override + public int getVertexCount() { + return vertices; + } + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/RecordingVertexConsumer.java b/src/main/java/com/jozufozu/flywheel/core/model/RecordingVertexConsumer.java deleted file mode 100644 index 127604939..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/model/RecordingVertexConsumer.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.jozufozu.flywheel.core.model; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.vertex.VertexConsumer; - -public class RecordingVertexConsumer implements VertexConsumer { - - List> replay = new ArrayList<>(); - - @Override - public VertexConsumer vertex(double x, double y, double z) { - replay.add(v -> v.vertex(x, y, z)); - return this; - } - - @Override - public VertexConsumer color(int r, int g, int b, int a) { - replay.add(v -> v.color(r, g, b, a)); - return this; - } - - @Override - public VertexConsumer uv(float u, float v) { - replay.add(vc -> vc.uv(u, v)); - return this; - } - - @Override - public VertexConsumer overlayCoords(int u, int v) { - replay.add(vc -> vc.overlayCoords(u, v)); - return this; - } - - @Override - public VertexConsumer uv2(int u, int v) { - replay.add(vc -> vc.uv2(u, v)); - return this; - } - - @Override - public VertexConsumer normal(float x, float y, float z) { - replay.add(v -> v.normal(x, y, z)); - return this; - } - - @Override - public void endVertex() { - replay.add(VertexConsumer::endVertex); - } - - @Override - public void defaultColor(int r, int g, int b, int a) { - replay.add(vc -> vc.defaultColor(r, g, b, a)); - } - - @Override - public void unsetDefaultColor() { - replay.add(VertexConsumer::unsetDefaultColor); - } - - public VertexRecording saveRecording() { - VertexRecording out = new VertexRecording(ImmutableList.copyOf(replay)); - replay.clear(); - return out; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java b/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java new file mode 100644 index 000000000..4d3aa6561 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java @@ -0,0 +1,468 @@ +package com.jozufozu.flywheel.core.model; + +import com.jozufozu.flywheel.util.ModelReader; +import com.jozufozu.flywheel.util.transform.Rotate; +import com.jozufozu.flywheel.util.transform.Scale; +import com.jozufozu.flywheel.util.transform.TStack; +import com.jozufozu.flywheel.util.transform.Translate; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.*; + +import it.unimi.dsi.fastutil.longs.Long2IntMap; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.level.Level; +import net.minecraftforge.client.model.pipeline.LightUtil; + +public class SuperByteBuffer implements Scale, Translate, Rotate, TStack { + + private final Model model; + private final ModelReader template; + + // Vertex Position + private final PoseStack transforms; + + private final Params defaultParams = Params.defaultParams(); + private final Params params = defaultParams.copy(); + + // Temporary + private static final Long2IntMap WORLD_LIGHT_CACHE = new Long2IntOpenHashMap(); + private final Vector4f pos = new Vector4f(); + private final Vector3f normal = new Vector3f(); + private final Vector4f lightPos = new Vector4f(); + + public SuperByteBuffer(Model model) { + this.model = model; + template = model.getReader(); + + transforms = new PoseStack(); + transforms.pushPose(); + } + + public void renderInto(PoseStack input, VertexConsumer builder) { + if (isEmpty()) + return; + + Matrix4f modelMat = input.last() + .pose() + .copy(); + Matrix4f localTransforms = transforms.last() + .pose(); + modelMat.multiply(localTransforms); + + Matrix3f normalMat; + + if (params.fullNormalTransform) { + normalMat = input.last().normal().copy(); + normalMat.mul(transforms.last().normal()); + } else { + normalMat = transforms.last().normal().copy(); + } + + if (params.useWorldLight) { + WORLD_LIGHT_CACHE.clear(); + } + + float f = .5f; + int vertexCount = template.getVertexCount(); + for (int i = 0; i < vertexCount; i++) { + float x = template.getX(i); + float y = template.getY(i); + float z = template.getZ(i); + pos.set(x, y, z, 1F); + pos.transform(modelMat); + builder.vertex(pos.x(), pos.y(), pos.z()); + + float normalX = template.getNX(i); + float normalY = template.getNY(i); + float normalZ = template.getNZ(i); + + normal.set(normalX, normalY, normalZ); + normal.transform(normalMat); + normal.normalize(); + float nx = normal.x(); + float ny = normal.y(); + float nz = normal.z(); + + float instanceDiffuse = LightUtil.diffuseLight(nx, ny, nz); + + switch (params.colorMode) { + case MODEL_ONLY -> builder.color(template.getR(i), template.getG(i), template.getB(i), template.getA(i)); + case DIFFUSE_ONLY -> builder.color(instanceDiffuse, instanceDiffuse, instanceDiffuse, 1f); + case MODEL_DIFFUSE -> { + int r = Byte.toUnsignedInt(template.getR(i)); + int g = Byte.toUnsignedInt(template.getG(i)); + int b = Byte.toUnsignedInt(template.getB(i)); + int a = Byte.toUnsignedInt(template.getA(i)); + + float diffuse = switch (params.diffuseMode) { + case NONE -> 1f; + case INSTANCE -> instanceDiffuse; + case ONE_OVER_STATIC -> 1 / LightUtil.diffuseLight(normalX, normalY, normalZ); + case INSTANCE_OVER_STATIC -> instanceDiffuse / LightUtil.diffuseLight(normalX, normalY, normalZ); + }; + + if (diffuse != 1) { + r = transformColor(r, diffuse); + g = transformColor(g, diffuse); + b = transformColor(b, diffuse); + } + + builder.color(r, g, b, a); + } + case RECOLOR -> { + if (params.diffuseMode == DiffuseMode.NONE) { + builder.color(params.r, params.g, params.b, params.a); + } else { + int colorR = transformColor(params.r, instanceDiffuse); + int colorG = transformColor(params.g, instanceDiffuse); + int colorB = transformColor(params.b, instanceDiffuse); + builder.color(colorR, colorG, colorB, params.a); + } + } + } + + //builder.color(Math.max(0, (int) (nx * 255)), Math.max(0, (int) (ny * 255)), Math.max(0, (int) (nz * 255)), 0xFF); + //builder.color(Math.max(0, (int) (normalX * 255)), Math.max(0, (int) (normalY * 255)), Math.max(0, (int) (normalZ * 255)), 0xFF); + + float u = template.getU(i); + float v = template.getV(i); + if (params.spriteShiftFunc != null) { + params.spriteShiftFunc.shift(builder, u, v); + } else { + builder.uv(u, v); + } + + if (params.hasOverlay) { + builder.overlayCoords(params.overlay); + } + + int light; + if (params.useWorldLight) { + lightPos.set(((x - f) * 15 / 16f) + f, (y - f) * 15 / 16f + f, (z - f) * 15 / 16f + f, 1F); + lightPos.transform(localTransforms); + if (params.lightTransform != null) { + lightPos.transform(params.lightTransform); + } + + light = getLight(Minecraft.getInstance().level, lightPos); + if (params.hasCustomLight) { + light = maxLight(light, params.packedLightCoords); + } + } else if (params.hasCustomLight) { + light = params.packedLightCoords; + } else { + light = template.getLight(i); + } + + if (params.hybridLight) { + builder.uv2(maxLight(light, template.getLight(i))); + } else { + builder.uv2(light); + } + + builder.normal(nx, ny, nz); + + builder.endVertex(); + } + + reset(); + } + + public SuperByteBuffer reset() { + while (!transforms.clear()) + transforms.popPose(); + transforms.pushPose(); + + params.load(defaultParams); + + return this; + } + + @Override + public SuperByteBuffer translate(double x, double y, double z) { + transforms.translate(x, y, z); + return this; + } + + @Override + public SuperByteBuffer multiply(Quaternion quaternion) { + transforms.mulPose(quaternion); + return this; + } + + public SuperByteBuffer transform(PoseStack stack) { + PoseStack.Pose last = stack.last(); + return transform(last.pose(), last.normal()); + } + + public SuperByteBuffer transform(Matrix4f pose, Matrix3f normal) { + transforms.last() + .pose() + .multiply(pose); + transforms.last() + .normal() + .mul(normal); + return this; + } + + public SuperByteBuffer rotateCentered(Direction axis, float radians) { + translate(.5f, .5f, .5f).rotate(axis, radians) + .translate(-.5f, -.5f, -.5f); + return this; + } + + public SuperByteBuffer rotateCentered(Quaternion q) { + translate(.5f, .5f, .5f).multiply(q) + .translate(-.5f, -.5f, -.5f); + return this; + } + + public SuperByteBuffer color(int r, int g, int b, int a) { + params.colorMode = ColorMode.RECOLOR; + params.r = r; + params.g = g; + params.b = b; + params.a = a; + return this; + } + + public SuperByteBuffer color(byte r, byte g, byte b, byte a) { + params.colorMode = ColorMode.RECOLOR; + params.r = Byte.toUnsignedInt(r); + params.g = Byte.toUnsignedInt(g); + params.b = Byte.toUnsignedInt(b); + params.a = Byte.toUnsignedInt(a); + return this; + } + + public SuperByteBuffer color(int color) { + params.colorMode = ColorMode.RECOLOR; + params.r = ((color >> 16) & 0xFF); + params.g = ((color >> 8) & 0xFF); + params.b = (color & 0xFF); + params.a = 255; + return this; + } + + public SuperByteBuffer shiftUV(SpriteShiftFunc entry) { + params.spriteShiftFunc = entry; + return this; + } + + public SuperByteBuffer overlay() { + params.hasOverlay = true; + return this; + } + + public SuperByteBuffer overlay(int overlay) { + params.hasOverlay = true; + params.overlay = overlay; + return this; + } + + /** + * Transforms normals not only by the local matrix stack, but also by the passed matrix stack. + */ + public SuperByteBuffer entityMode() { + params.hasOverlay = true; + params.fullNormalTransform = true; + params.diffuseMode = DiffuseMode.NONE; + params.colorMode = ColorMode.RECOLOR; + return this; + } + + public SuperByteBuffer light() { + params.useWorldLight = true; + return this; + } + + public SuperByteBuffer light(Matrix4f lightTransform) { + params.useWorldLight = true; + params.lightTransform = lightTransform; + return this; + } + + public SuperByteBuffer light(int packedLightCoords) { + params.hasCustomLight = true; + params.packedLightCoords = packedLightCoords; + return this; + } + + public SuperByteBuffer light(Matrix4f lightTransform, int packedLightCoords) { + light(lightTransform); + light(packedLightCoords); + return this; + } + + /** + * Uses max light from calculated light (world light or custom light) and vertex light for the final light value. + * Ineffective if any other light method was not called. + */ + public SuperByteBuffer hybridLight() { + params.hybridLight = true; + return this; + } + + public boolean isEmpty() { + return template.isEmpty(); + } + + @Override + public SuperByteBuffer scale(float factorX, float factorY, float factorZ) { + transforms.scale(factorX, factorY, factorZ); + return this; + } + + @Override + public SuperByteBuffer pushPose() { + transforms.pushPose(); + return this; + } + + @Override + public SuperByteBuffer popPose() { + transforms.popPose(); + return this; + } + + @Override + public String toString() { + return "SuperByteBuffer[" + model + ']'; + } + + public static int transformColor(int component, float scale) { + return Mth.clamp((int) (component * scale), 0, 255); + } + + public static int maxLight(int packedLight1, int packedLight2) { + int blockLight1 = LightTexture.block(packedLight1); + int skyLight1 = LightTexture.sky(packedLight1); + int blockLight2 = LightTexture.block(packedLight2); + int skyLight2 = LightTexture.sky(packedLight2); + return LightTexture.pack(Math.max(blockLight1, blockLight2), Math.max(skyLight1, skyLight2)); + } + + private static int getLight(Level world, Vector4f lightPos) { + BlockPos pos = new BlockPos(lightPos.x(), lightPos.y(), lightPos.z()); + return WORLD_LIGHT_CACHE.computeIfAbsent(pos.asLong(), $ -> LevelRenderer.getLightColor(world, pos)); + } + + @FunctionalInterface + public interface SpriteShiftFunc { + void shift(VertexConsumer builder, float u, float v); + } + + public enum ColorMode { + MODEL_ONLY, + MODEL_DIFFUSE, + DIFFUSE_ONLY, + RECOLOR + } + + public enum DiffuseMode { + NONE, + INSTANCE, + ONE_OVER_STATIC, + INSTANCE_OVER_STATIC, + } + + public static class Params { + // Vertex Coloring + public ColorMode colorMode = ColorMode.DIFFUSE_ONLY; + public DiffuseMode diffuseMode = DiffuseMode.INSTANCE; + public int r; + public int g; + public int b; + public int a; + + // Vertex Texture Coords + public SpriteShiftFunc spriteShiftFunc; + + // Vertex Overlay Color + public boolean hasOverlay; + public int overlay = OverlayTexture.NO_OVERLAY; + + // Vertex Lighting + public boolean useWorldLight; + public Matrix4f lightTransform; + public boolean hasCustomLight; + public int packedLightCoords; + public boolean hybridLight; + + // Vertex Normals + public boolean fullNormalTransform; + + public void load(Params from) { + colorMode = from.colorMode; + diffuseMode = from.diffuseMode; + r = from.r; + g = from.g; + b = from.b; + a = from.a; + spriteShiftFunc = from.spriteShiftFunc; + hasOverlay = from.hasOverlay; + overlay = from.overlay; + useWorldLight = from.useWorldLight; + lightTransform = from.lightTransform; + hasCustomLight = from.hasCustomLight; + packedLightCoords = from.packedLightCoords; + hybridLight = from.hybridLight; + fullNormalTransform = from.fullNormalTransform; + } + + public Params copy() { + Params params = new Params(); + params.load(this); + return params; + } + + public static Params defaultParams() { + Params out = new Params(); + out.colorMode = ColorMode.DIFFUSE_ONLY; + out.diffuseMode = DiffuseMode.INSTANCE; + out.r = 0xFF; + out.g = 0xFF; + out.b = 0xFF; + out.a = 0xFF; + out.spriteShiftFunc = null; + out.hasOverlay = false; + out.overlay = OverlayTexture.NO_OVERLAY; + out.useWorldLight = false; + out.lightTransform = null; + out.hasCustomLight = false; + out.packedLightCoords = 0; + out.hybridLight = false; + out.fullNormalTransform = false; + return out; + } + + public static Params newEntityParams() { + Params out = new Params(); + out.colorMode = ColorMode.RECOLOR; + out.diffuseMode = DiffuseMode.NONE; + out.r = 0xFF; + out.g = 0xFF; + out.b = 0xFF; + out.a = 0xFF; + out.spriteShiftFunc = null; + out.hasOverlay = true; + out.overlay = OverlayTexture.NO_OVERLAY; + out.useWorldLight = false; + out.lightTransform = null; + out.hasCustomLight = false; + out.packedLightCoords = 0; + out.hybridLight = false; + out.fullNormalTransform = true; + return out; + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java b/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java index fefc5cf80..c1ea4957c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java @@ -2,6 +2,8 @@ package com.jozufozu.flywheel.core.model; import static com.jozufozu.flywheel.util.RenderMath.nb; +import java.nio.ByteBuffer; + import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -9,6 +11,10 @@ public class VecBufferWriter implements VertexConsumer { private final VecBuffer buffer; + public VecBufferWriter(ByteBuffer buffer) { + this.buffer = new VecBuffer(buffer); + } + public VecBufferWriter(VecBuffer buffer) { this.buffer = buffer; } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/VertexRecording.java b/src/main/java/com/jozufozu/flywheel/core/model/VertexRecording.java deleted file mode 100644 index 82df180ff..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/model/VertexRecording.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.jozufozu.flywheel.core.model; - -import java.util.function.Consumer; - -import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.vertex.VertexConsumer; - -public record VertexRecording(ImmutableList> recording) { - - public void replay(VertexConsumer vc) { - for (Consumer consumer : recording) { - consumer.accept(vc); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java index c9c355c05..c5f5dff2e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java @@ -5,6 +5,7 @@ import java.util.Collection; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.util.BufferBuilderReader; +import com.jozufozu.flywheel.util.ModelReader; import com.jozufozu.flywheel.util.RenderMath; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -15,7 +16,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp public class WorldModel implements Model { - private final BufferBuilderReader reader; + private final ModelReader reader; private final String name; public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection blocks, String name) { @@ -33,7 +34,7 @@ public class WorldModel implements Model { for (int i = 0; i < vertexCount(); i++) { vertices.vertex(reader.getX(i), reader.getY(i), reader.getZ(i)); - vertices.normal(RenderMath.f(reader.getNX(i)), RenderMath.f(reader.getNY(i)), RenderMath.f(reader.getNZ(i))); + vertices.normal(reader.getNX(i), reader.getNY(i), reader.getNZ(i)); vertices.uv(reader.getU(i), reader.getV(i)); @@ -60,4 +61,8 @@ public class WorldModel implements Model { return Formats.COLORED_LIT_MODEL; } + @Override + public ModelReader getReader() { + return reader; + } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java new file mode 100644 index 000000000..a22223d2f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java @@ -0,0 +1,58 @@ +package com.jozufozu.flywheel.mixin; + +import java.nio.ByteBuffer; + +import javax.annotation.Nullable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; +import com.jozufozu.flywheel.backend.model.DirectBufferBuilder; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormatElement; + +@Mixin(BufferBuilder.class) +public abstract class BufferBuilderMixin implements DirectBufferBuilder { + @Shadow + private ByteBuffer buffer; + + @Shadow + private VertexFormat format; + + @Shadow + protected abstract void ensureCapacity(int p_85723_); + + @Shadow + private int vertices; + + @Shadow + @Nullable + private VertexFormatElement currentElement; + + @Shadow + private int elementIndex; + + @Shadow + private int nextElementByte; + + @Override + public DirectVertexConsumer intoDirectConsumer(int neededVerts) { + ensureCapacity(neededVerts * this.format.getVertexSize()); + return new DirectVertexConsumer(this.buffer, this.format); + } + + @Override + public void updateAfterWriting(DirectVertexConsumer complete) { + int vertexCount = complete.getVertexCount(); + int totalWrittenBytes = vertexCount * format.getVertexSize(); + + this.vertices += vertexCount; + this.currentElement = format.getElements() + .get(0); + this.elementIndex = 0; + this.nextElementByte += totalWrittenBytes; + this.buffer.position(complete.startPos + totalWrittenBytes); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/BufferBuilderReader.java b/src/main/java/com/jozufozu/flywheel/util/BufferBuilderReader.java index 640506bb6..eecf24e1b 100644 --- a/src/main/java/com/jozufozu/flywheel/util/BufferBuilderReader.java +++ b/src/main/java/com/jozufozu/flywheel/util/BufferBuilderReader.java @@ -6,7 +6,7 @@ import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.datafixers.util.Pair; -public class BufferBuilderReader { +public class BufferBuilderReader implements ModelReader { private final ByteBuffer buffer; private final int vertexCount; @@ -35,66 +35,81 @@ public class BufferBuilderReader { // } } + @Override public boolean isEmpty() { return vertexCount == 0; } - public int vertIdx(int vertexIndex) { + private int vertIdx(int vertexIndex) { return vertexIndex * formatSize; } + @Override public float getX(int index) { return buffer.getFloat(vertIdx(index)); } + @Override public float getY(int index) { return buffer.getFloat(vertIdx(index) + 4); } + @Override public float getZ(int index) { return buffer.getFloat(vertIdx(index) + 8); } + @Override public byte getR(int index) { return buffer.get(vertIdx(index) + 12); } + @Override public byte getG(int index) { return buffer.get(vertIdx(index) + 13); } + @Override public byte getB(int index) { return buffer.get(vertIdx(index) + 14); } + @Override public byte getA(int index) { return buffer.get(vertIdx(index) + 15); } + @Override public float getU(int index) { return buffer.getFloat(vertIdx(index) + 16); } + @Override public float getV(int index) { return buffer.getFloat(vertIdx(index) + 20); } + @Override public int getLight(int index) { return buffer.getInt(vertIdx(index) + 24); } - public byte getNX(int index) { - return buffer.get(vertIdx(index) + 28); + @Override + public float getNX(int index) { + return RenderMath.f(buffer.get(vertIdx(index) + 28)); } - public byte getNY(int index) { - return buffer.get(vertIdx(index) + 29); + @Override + public float getNY(int index) { + return RenderMath.f(buffer.get(vertIdx(index) + 29)); } - public byte getNZ(int index) { - return buffer.get(vertIdx(index) + 30); + @Override + public float getNZ(int index) { + return RenderMath.f(buffer.get(vertIdx(index) + 30)); } + @Override public int getVertexCount() { return vertexCount; } diff --git a/src/main/java/com/jozufozu/flywheel/util/ModelReader.java b/src/main/java/com/jozufozu/flywheel/util/ModelReader.java new file mode 100644 index 000000000..c1dda5f8b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/ModelReader.java @@ -0,0 +1,35 @@ +package com.jozufozu.flywheel.util; + +public interface ModelReader { + float getX(int index); + + float getY(int index); + + float getZ(int index); + + byte getR(int index); + + byte getG(int index); + + byte getB(int index); + + byte getA(int index); + + float getU(int index); + + float getV(int index); + + int getLight(int index); + + float getNX(int index); + + float getNY(int index); + + float getNZ(int index); + + int getVertexCount(); + + default boolean isEmpty() { + return getVertexCount() == 0; + } +} diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index 537f77cd1..7bdf36429 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -5,6 +5,7 @@ "compatibilityLevel": "JAVA_17", "refmap": "flywheel.refmap.json", "client": [ + "BufferBuilderMixin", "CancelEntityRenderMixin", "ChunkRebuildHooksMixin", "FixFabulousDepthMixin", From 3767390c9672224b61efeaee567b02d3532a6945 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 16 Dec 2021 00:11:47 -0800 Subject: [PATCH 02/22] Better SBB params - Transform interface for grouping traits and combined behavior - Move transforms to params, impl Transform - Pass Params object to BatchingTransformers instead of SBB --- .../api/struct/BatchingTransformer.java | 2 +- .../instancing/batching/CPUInstancer.java | 10 +- .../core/materials/model/ModelData.java | 16 +- .../flywheel/core/model/SuperByteBuffer.java | 340 ++++++++---------- .../flywheel/util/transform/Transform.java | 37 ++ 5 files changed, 213 insertions(+), 192 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/util/transform/Transform.java diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java b/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java index 4b7e9f5d7..53e0dedb0 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java @@ -5,5 +5,5 @@ import com.jozufozu.flywheel.core.model.SuperByteBuffer; @FunctionalInterface public interface BatchingTransformer { - void transform(S s, SuperByteBuffer b); + void transform(S s, SuperByteBuffer.Params b); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 38c5a7510..307f3315b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -36,10 +36,14 @@ public class CPUInstancer extends AbstractInstancer { renderSetup(); - for (D d : data) { - if (context.usesOverlay()) sbb.entityMode(); + if (context.usesOverlay()) { + sbb.getDefaultParams().entityMode(); + } - transform.transform(d, sbb); + sbb.reset(); + + for (D d : data) { + transform.transform(d, sbb.getParams()); sbb.renderInto(stack, buffer); } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelData.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelData.java index 0b5b2590a..f8ca670bd 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelData.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelData.java @@ -1,8 +1,10 @@ package com.jozufozu.flywheel.core.materials.model; import com.jozufozu.flywheel.core.materials.BasicData; +import com.jozufozu.flywheel.core.model.SuperByteBuffer; import com.jozufozu.flywheel.util.transform.Rotate; import com.jozufozu.flywheel.util.transform.Scale; +import com.jozufozu.flywheel.util.transform.Transform; import com.jozufozu.flywheel.util.transform.Translate; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Matrix3f; @@ -11,7 +13,7 @@ import com.mojang.math.Quaternion; import net.minecraft.util.Mth; -public class ModelData extends BasicData implements Translate, Rotate, Scale { +public class ModelData extends BasicData implements Transform { public final Matrix4f model = new Matrix4f(); public final Matrix3f normal = new Matrix3f(); @@ -83,4 +85,16 @@ public class ModelData extends BasicData implements Translate, Rotate model.multiplyWithTranslation((float) x, (float) y, (float) z); return this; } + + @Override + public ModelData mulPose(Matrix4f pose) { + this.model.multiply(pose); + return this; + } + + @Override + public ModelData mulNormal(Matrix3f normal) { + this.normal.mul(normal); + return this; + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java b/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java index 4d3aa6561..5135885b5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java @@ -1,10 +1,7 @@ package com.jozufozu.flywheel.core.model; import com.jozufozu.flywheel.util.ModelReader; -import com.jozufozu.flywheel.util.transform.Rotate; -import com.jozufozu.flywheel.util.transform.Scale; -import com.jozufozu.flywheel.util.transform.TStack; -import com.jozufozu.flywheel.util.transform.Translate; +import com.jozufozu.flywheel.util.transform.Transform; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.*; @@ -15,24 +12,27 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.texture.OverlayTexture; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraftforge.client.model.pipeline.LightUtil; -public class SuperByteBuffer implements Scale, Translate, Rotate, TStack { +public class SuperByteBuffer { private final Model model; private final ModelReader template; - // Vertex Position - private final PoseStack transforms; - private final Params defaultParams = Params.defaultParams(); private final Params params = defaultParams.copy(); + public Params getDefaultParams() { + return defaultParams; + } + + public Params getParams() { + return params; + } + // Temporary private static final Long2IntMap WORLD_LIGHT_CACHE = new Long2IntOpenHashMap(); private final Vector4f pos = new Vector4f(); @@ -42,9 +42,6 @@ public class SuperByteBuffer implements Scale, Translate, Translate, Translate, Translate> 16) & 0xFF); - params.g = ((color >> 8) & 0xFF); - params.b = (color & 0xFF); - params.a = 255; - return this; - } - - public SuperByteBuffer shiftUV(SpriteShiftFunc entry) { - params.spriteShiftFunc = entry; - return this; - } - - public SuperByteBuffer overlay() { - params.hasOverlay = true; - return this; - } - - public SuperByteBuffer overlay(int overlay) { - params.hasOverlay = true; - params.overlay = overlay; - return this; - } - - /** - * Transforms normals not only by the local matrix stack, but also by the passed matrix stack. - */ - public SuperByteBuffer entityMode() { - params.hasOverlay = true; - params.fullNormalTransform = true; - params.diffuseMode = DiffuseMode.NONE; - params.colorMode = ColorMode.RECOLOR; - return this; - } - - public SuperByteBuffer light() { - params.useWorldLight = true; - return this; - } - - public SuperByteBuffer light(Matrix4f lightTransform) { - params.useWorldLight = true; - params.lightTransform = lightTransform; - return this; - } - - public SuperByteBuffer light(int packedLightCoords) { - params.hasCustomLight = true; - params.packedLightCoords = packedLightCoords; - return this; - } - - public SuperByteBuffer light(Matrix4f lightTransform, int packedLightCoords) { - light(lightTransform); - light(packedLightCoords); - return this; - } - - /** - * Uses max light from calculated light (world light or custom light) and vertex light for the final light value. - * Ineffective if any other light method was not called. - */ - public SuperByteBuffer hybridLight() { - params.hybridLight = true; - return this; - } - public boolean isEmpty() { return template.isEmpty(); } - @Override - public SuperByteBuffer scale(float factorX, float factorY, float factorZ) { - transforms.scale(factorX, factorY, factorZ); - return this; - } - - @Override - public SuperByteBuffer pushPose() { - transforms.pushPose(); - return this; - } - - @Override - public SuperByteBuffer popPose() { - transforms.popPose(); - return this; - } - @Override public String toString() { return "SuperByteBuffer[" + model + ']'; @@ -375,7 +223,11 @@ public class SuperByteBuffer implements Scale, Translate { + // Vertex Position + public final Matrix4f model = new Matrix4f(); + public final Matrix3f normal = new Matrix3f(); + // Vertex Coloring public ColorMode colorMode = ColorMode.DIFFUSE_ONLY; public DiffuseMode diffuseMode = DiffuseMode.INSTANCE; @@ -402,6 +254,8 @@ public class SuperByteBuffer implements Scale, Translate, Translate> 16) & 0xFF); + this.g = ((color >> 8) & 0xFF); + this.b = (color & 0xFF); + this.a = 255; + return this; + } + + public Params shiftUV(SpriteShiftFunc entry) { + this.spriteShiftFunc = entry; + return this; + } + + public Params overlay() { + this.hasOverlay = true; + return this; + } + + public Params overlay(int overlay) { + this.hasOverlay = true; + this.overlay = overlay; + return this; + } + + /** + * Transforms normals not only by the local matrix stack, but also by the passed matrix stack. + */ + public Params entityMode() { + this.hasOverlay = true; + this.fullNormalTransform = true; + this.diffuseMode = DiffuseMode.NONE; + this.colorMode = ColorMode.RECOLOR; + return this; + } + + public Params light() { + this.useWorldLight = true; + return this; + } + + public Params light(Matrix4f lightTransform) { + this.useWorldLight = true; + this.lightTransform = lightTransform; + return this; + } + + public Params light(int packedLightCoords) { + this.hasCustomLight = true; + this.packedLightCoords = packedLightCoords; + return this; + } + + public Params light(Matrix4f lightTransform, int packedLightCoords) { + light(lightTransform); + light(packedLightCoords); + return this; + } + + /** + * Uses max light from calculated light (world light or custom light) and vertex light for the final light value. + * Ineffective if any other light method was not called. + */ + public Params hybridLight() { + hybridLight = true; + return this; + } + + @Override + public Params multiply(Quaternion quaternion) { + model.multiply(quaternion); + return this; + } + + @Override + public Params scale(float pX, float pY, float pZ) { + model.multiply(Matrix4f.createScaleMatrix(pX, pY, pZ)); + if (pX == pY && pY == pZ) { + if (pX > 0.0F) { + return this; + } + + normal.mul(-1.0F); + } + + float f = 1.0F / pX; + float f1 = 1.0F / pY; + float f2 = 1.0F / pZ; + float f3 = Mth.fastInvCubeRoot(f * f1 * f2); + normal.mul(Matrix3f.createScaleMatrix(f3 * f, f3 * f1, f3 * f2)); + return this; + } + + @Override + public Params translate(double x, double y, double z) { + model.multiplyWithTranslation((float) x, (float) y, (float) z); + + return this; + } + + @Override + public Params mulPose(Matrix4f pose) { + this.model.multiply(pose); + return this; + } + + @Override + public Params mulNormal(Matrix3f normal) { + this.normal.mul(normal); + return this; + } + public static Params defaultParams() { Params out = new Params(); + out.model.setIdentity(); + out.normal.setIdentity(); out.colorMode = ColorMode.DIFFUSE_ONLY; out.diffuseMode = DiffuseMode.INSTANCE; out.r = 0xFF; @@ -444,25 +430,5 @@ public class SuperByteBuffer implements Scale, Translate> extends Translate, Rotate, Scale { + Self mulPose(Matrix4f pose); + + Self mulNormal(Matrix3f normal); + + default Self transform(Matrix4f pose, Matrix3f normal) { + mulPose(pose); + return mulNormal(normal); + } + + default Self transform(PoseStack stack) { + PoseStack.Pose last = stack.last(); + return transform(last.pose(), last.normal()); + } + + default Self rotateCentered(Direction axis, float radians) { + translate(.5f, .5f, .5f).rotate(axis, radians) + .translate(-.5f, -.5f, -.5f); + return (Self) this; + } + + default Self rotateCentered(Quaternion q) { + translate(.5f, .5f, .5f).multiply(q) + .translate(-.5f, -.5f, -.5f); + return (Self) this; + } +} From be3f47dfbd27c170c5645ebb62900e3355c17caf Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 16 Dec 2021 10:49:26 -0800 Subject: [PATCH 03/22] Naive parallelism - Submit many tasks to executor and then wait for them all to complete. - Use WaitGroup - SBB no longer stores params, instead accepts Params arg to render - SBB keeps scratch variables local - CPUInstancer keeps track of default params - Separate #setup and #draw... functions in CPUInstancer - Combine DirectBufferBuilder#updateAfterWriting with #intoDirectConsumer - DirectVertexConsumer#split to distribute work --- .../backend/instancing/AbstractInstancer.java | 4 ++ .../instancing/batching/BatchExecutor.java | 34 ++++++++++ .../instancing/batching/BatchedMaterial.java | 3 +- .../batching/BatchedMaterialGroup.java | 14 +++-- .../instancing/batching/BatchingEngine.java | 13 +++- .../instancing/batching/CPUInstancer.java | 63 ++++++++++++++----- .../instancing/batching/WaitGroup.java | 24 +++++++ .../backend/model/DirectBufferBuilder.java | 4 +- .../backend/model/DirectVertexConsumer.java | 38 ++++++++--- .../flywheel/core/model/SuperByteBuffer.java | 28 ++------- .../flywheel/mixin/BufferBuilderMixin.java | 18 +++--- 11 files changed, 179 insertions(+), 64 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchExecutor.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/batching/WaitGroup.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index b6e85dbdc..06e6071fd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -66,6 +66,10 @@ public abstract class AbstractInstancer implements Insta return data.size(); } + public int getTotalVertexCount() { + return getModelVertexCount() * numInstances(); + } + protected BitSet getDirtyBitSet() { final int size = data.size(); final BitSet dirtySet = new BitSet(size); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchExecutor.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchExecutor.java new file mode 100644 index 000000000..60e41f2b4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchExecutor.java @@ -0,0 +1,34 @@ +package com.jozufozu.flywheel.backend.instancing.batching; + +import java.util.concurrent.Executor; + +import org.jetbrains.annotations.NotNull; + +public class BatchExecutor implements Executor { + private final Executor internal; + private final WaitGroup wg; + + public BatchExecutor(Executor internal) { + this.internal = internal; + + wg = new WaitGroup(); + } + + @Override + public void execute(@NotNull Runnable command) { + wg.add(1); + internal.execute(() -> { + // wrapper function to decrement the wait group + try { + command.run(); + } catch (Exception ignored) { + } finally { + wg.done(); + } + }); + } + + public void await() throws InterruptedException { + wg.await(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java index ad14cba40..18183fa6a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java @@ -30,7 +30,8 @@ public class BatchedMaterial implements Material { public void render(PoseStack stack, VertexConsumer buffer, FormatContext context) { for (CPUInstancer instancer : models.values()) { - instancer.drawAll(stack, buffer, context); + instancer.setup(context); + instancer.drawAll(stack, buffer); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java index ea7b65e7c..34f4b71d6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing.batching; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.Executor; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.MaterialGroup; @@ -30,15 +31,20 @@ public class BatchedMaterialGroup implements MaterialGroup { return (BatchedMaterial) materials.computeIfAbsent(spec, BatchedMaterial::new); } - public void render(PoseStack stack, MultiBufferSource source) { + public void render(PoseStack stack, MultiBufferSource source, Executor pool) { VertexConsumer buffer = source.getBuffer(state); if (buffer instanceof DirectBufferBuilder direct) { DirectVertexConsumer consumer = direct.intoDirectConsumer(calculateNeededVertices()); + FormatContext context = new FormatContext(consumer.hasOverlay()); - renderInto(stack, consumer, new FormatContext(consumer.hasOverlay())); + for (BatchedMaterial material : materials.values()) { + for (CPUInstancer instancer : material.models.values()) { + instancer.setup(context); - direct.updateAfterWriting(consumer); + instancer.submitTasks(stack, pool, consumer); + } + } } else { renderInto(stack, buffer, FormatContext.defaultContext()); } @@ -48,7 +54,7 @@ public class BatchedMaterialGroup implements MaterialGroup { int total = 0; for (BatchedMaterial material : materials.values()) { for (CPUInstancer instancer : material.models.values()) { - total += instancer.getModelVertexCount() * instancer.numInstances(); + total += instancer.getTotalVertexCount(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index ba915499e..cc5621418 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -3,6 +3,8 @@ package com.jozufozu.flywheel.backend.instancing.batching; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.backend.RenderLayer; @@ -22,11 +24,15 @@ public class BatchingEngine implements Engine { protected final Map> layers; + private final BatchExecutor pool; + public BatchingEngine() { this.layers = new EnumMap<>(RenderLayer.class); for (RenderLayer value : RenderLayer.values()) { layers.put(value, new HashMap<>()); } + + pool = new BatchExecutor(Executors.newWorkStealingPool(ForkJoinPool.getCommonPoolParallelism())); } @Override @@ -50,7 +56,12 @@ public class BatchingEngine implements Engine { for (Map.Entry entry : layers.get(event.getLayer()).entrySet()) { BatchedMaterialGroup group = entry.getValue(); - group.render(stack, buffers); + group.render(stack, buffers, pool); + } + + try { + pool.await(); + } catch (InterruptedException ignored) { } stack.popPose(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 307f3315b..4231c2759 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -1,27 +1,51 @@ package com.jozufozu.flywheel.backend.instancing.batching; +import java.util.concurrent.Executor; + import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.BatchingTransformer; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; +import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.SuperByteBuffer; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.blaze3d.vertex.VertexFormat; public class CPUInstancer extends AbstractInstancer { private final BatchingTransformer transform; private final SuperByteBuffer sbb; + private final SuperByteBuffer.Params defaultParams; public CPUInstancer(StructType type, Model modelData) { super(type, modelData); sbb = new SuperByteBuffer(modelData); + defaultParams = SuperByteBuffer.Params.defaultParams(); transform = type.asBatched() .getTransformer(); + + if (transform == null) { + throw new NullPointerException("Cannot batch " + type.toString()); + } + } + + void submitTasks(PoseStack stack, Executor pool, DirectVertexConsumer consumer) { + int instances = numInstances(); + + while (instances > 0) { + int end = instances; + instances -= 100; + int start = Math.max(instances, 0); + + int verts = getModelVertexCount() * (end - start); + + DirectVertexConsumer sub = consumer.split(verts); + + pool.execute(() -> drawRange(stack, sub, start, end)); + } } @Override @@ -29,23 +53,34 @@ public class CPUInstancer extends AbstractInstancer { // noop } - public void drawAll(PoseStack stack, VertexConsumer buffer, FormatContext context) { - if (transform == null) { - return; - } + private void drawRange(PoseStack stack, VertexConsumer buffer, int from, int to) { + SuperByteBuffer.Params params = defaultParams.copy(); + for (D d : data.subList(from, to)) { + transform.transform(d, params); + + sbb.renderInto(params, stack, buffer); + + params.load(defaultParams); + } + } + + void drawAll(PoseStack stack, VertexConsumer buffer) { + SuperByteBuffer.Params params = defaultParams.copy(); + for (D d : data) { + transform.transform(d, params); + + sbb.renderInto(params, stack, buffer); + + params.load(defaultParams); + } + } + + void setup(FormatContext context) { renderSetup(); if (context.usesOverlay()) { - sbb.getDefaultParams().entityMode(); - } - - sbb.reset(); - - for (D d : data) { - transform.transform(d, sbb.getParams()); - - sbb.renderInto(stack, buffer); + defaultParams.entityMode(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/WaitGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/WaitGroup.java new file mode 100644 index 000000000..df7ffb7d9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/WaitGroup.java @@ -0,0 +1,24 @@ +package com.jozufozu.flywheel.backend.instancing.batching; + +// https://stackoverflow.com/questions/29655531 +public class WaitGroup { + + private int jobs = 0; + + public synchronized void add(int i) { + jobs += i; + } + + public synchronized void done() { + if (--jobs == 0) { + notifyAll(); + } + } + + public synchronized void await() throws InterruptedException { + while (jobs > 0) { + wait(); + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java index b3f08681a..98449155a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java @@ -2,7 +2,5 @@ package com.jozufozu.flywheel.backend.model; public interface DirectBufferBuilder { - DirectVertexConsumer intoDirectConsumer(int neededVerts); - - void updateAfterWriting(DirectVertexConsumer complete); + DirectVertexConsumer intoDirectConsumer(int vertexCount); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java index 0bea53cb4..e0516e69c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java @@ -10,7 +10,6 @@ import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormatElement; public class DirectVertexConsumer implements VertexConsumer { - public final VertexFormat format; private final int stride; public final int startPos; @@ -23,7 +22,6 @@ public class DirectVertexConsumer implements VertexConsumer { private int uv2 = -1; private long vertexBase; - private int vertexCount; public DirectVertexConsumer(ByteBuffer buffer, VertexFormat format) { this.format = format; @@ -49,13 +47,42 @@ public class DirectVertexConsumer implements VertexConsumer { offset += element.getByteSize(); } - this.vertexBase = MemoryUtil.memAddress(buffer); + this.vertexBase = MemoryUtil.memAddress(buffer, startPos); + } + + private DirectVertexConsumer(DirectVertexConsumer parent) { + this.format = parent.format; + this.stride = parent.stride; + this.startPos = parent.startPos; + this.position = parent.position; + this.normal = parent.normal; + this.color = parent.color; + this.uv = parent.uv; + this.uv1 = parent.uv1; + this.uv2 = parent.uv2; + + this.vertexBase = parent.vertexBase; } public boolean hasOverlay() { return uv1 >= 0; } + /** + * Split off the head of this consumer into a new object and advance our write-pointer. + * @param vertexCount The number of vertices that must be written to the head. + * @return The head of this consumer. + */ + public DirectVertexConsumer split(int vertexCount) { + int bytes = vertexCount * stride; + + DirectVertexConsumer head = new DirectVertexConsumer(this); + + this.vertexBase += bytes; + + return head; + } + @Override public VertexConsumer vertex(double x, double y, double z) { if (position < 0) return this; @@ -117,7 +144,6 @@ public class DirectVertexConsumer implements VertexConsumer { @Override public void endVertex() { vertexBase += stride; - vertexCount++; } @Override @@ -129,8 +155,4 @@ public class DirectVertexConsumer implements VertexConsumer { public void unsetDefaultColor() { } - - public int getVertexCount() { - return vertexCount; - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java b/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java index 5135885b5..daf2f3440 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java @@ -22,32 +22,22 @@ public class SuperByteBuffer { private final Model model; private final ModelReader template; - private final Params defaultParams = Params.defaultParams(); - private final Params params = defaultParams.copy(); - - public Params getDefaultParams() { - return defaultParams; - } - - public Params getParams() { - return params; - } - // Temporary private static final Long2IntMap WORLD_LIGHT_CACHE = new Long2IntOpenHashMap(); - private final Vector4f pos = new Vector4f(); - private final Vector3f normal = new Vector3f(); - private final Vector4f lightPos = new Vector4f(); public SuperByteBuffer(Model model) { this.model = model; template = model.getReader(); } - public void renderInto(PoseStack input, VertexConsumer builder) { + public void renderInto(Params params, PoseStack input, VertexConsumer builder) { if (isEmpty()) return; + Vector4f pos = new Vector4f(); + Vector3f normal = new Vector3f(); + Vector4f lightPos = new Vector4f(); + Matrix4f modelMat = input.last() .pose() .copy(); @@ -168,14 +158,6 @@ public class SuperByteBuffer { builder.endVertex(); } - - reset(); - } - - public SuperByteBuffer reset() { - params.load(defaultParams); - - return this; } public boolean isEmpty() { diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java index a22223d2f..95d2874e3 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java @@ -38,21 +38,19 @@ public abstract class BufferBuilderMixin implements DirectBufferBuilder { private int nextElementByte; @Override - public DirectVertexConsumer intoDirectConsumer(int neededVerts) { - ensureCapacity(neededVerts * this.format.getVertexSize()); - return new DirectVertexConsumer(this.buffer, this.format); - } + public DirectVertexConsumer intoDirectConsumer(int vertexCount) { + int bytes = vertexCount * format.getVertexSize(); + ensureCapacity(bytes); - @Override - public void updateAfterWriting(DirectVertexConsumer complete) { - int vertexCount = complete.getVertexCount(); - int totalWrittenBytes = vertexCount * format.getVertexSize(); + DirectVertexConsumer consumer = new DirectVertexConsumer(this.buffer, this.format); this.vertices += vertexCount; this.currentElement = format.getElements() .get(0); this.elementIndex = 0; - this.nextElementByte += totalWrittenBytes; - this.buffer.position(complete.startPos + totalWrittenBytes); + this.nextElementByte += bytes; + this.buffer.position(consumer.startPos + bytes); + + return consumer; } } From 431ff92861db81455636965d671065e76a0b9b9e Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 16 Dec 2021 23:10:24 -0800 Subject: [PATCH 04/22] Simplify SBB - SBB -> ModelTransformer - Shader binding the minecraft way - Engines are responsible for ending batches --- .../api/struct/BatchingTransformer.java | 4 +- .../flywheel/backend/gl/shader/GlProgram.java | 10 +- .../batching/BatchedMaterialGroup.java | 39 ++-- .../instancing/batching/BatchingEngine.java | 2 + .../instancing/batching/CPUInstancer.java | 31 ++- .../backend/model/DirectVertexConsumer.java | 19 +- .../core/materials/model/ModelData.java | 4 - ...rByteBuffer.java => ModelTransformer.java} | 146 +++--------- .../flywheel/mixin/BufferBuilderMixin.java | 2 +- .../flywheel/mixin/RenderHooksMixin.java | 6 - .../mixin/ShaderInstanceAccessor.java | 14 ++ .../flywheel/util/BakedQuadWrapper.java | 219 ------------------ .../com/jozufozu/flywheel/util/ChunkUtil.java | 18 -- .../flywheel/util/transform/Transform.java | 1 - src/main/resources/flywheel.mixins.json | 1 + 15 files changed, 112 insertions(+), 404 deletions(-) rename src/main/java/com/jozufozu/flywheel/core/model/{SuperByteBuffer.java => ModelTransformer.java} (65%) create mode 100644 src/main/java/com/jozufozu/flywheel/mixin/ShaderInstanceAccessor.java delete mode 100644 src/main/java/com/jozufozu/flywheel/util/BakedQuadWrapper.java delete mode 100644 src/main/java/com/jozufozu/flywheel/util/ChunkUtil.java diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java b/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java index 53e0dedb0..06ca11419 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java @@ -1,9 +1,9 @@ package com.jozufozu.flywheel.api.struct; -import com.jozufozu.flywheel.core.model.SuperByteBuffer; +import com.jozufozu.flywheel.core.model.ModelTransformer; @FunctionalInterface public interface BatchingTransformer { - void transform(S s, SuperByteBuffer.Params b); + void transform(S s, ModelTransformer.Params b); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java index 8f9efd7f0..2536ba21b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java @@ -4,7 +4,6 @@ import static org.lwjgl.opengl.GL20.glDeleteProgram; import static org.lwjgl.opengl.GL20.glGetUniformLocation; import static org.lwjgl.opengl.GL20.glUniform1i; import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; -import static org.lwjgl.opengl.GL20.glUseProgram; import java.nio.FloatBuffer; @@ -12,6 +11,8 @@ import org.lwjgl.system.MemoryStack; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlObject; +import com.jozufozu.flywheel.mixin.ShaderInstanceAccessor; +import com.mojang.blaze3d.shaders.ProgramManager; import com.mojang.math.Matrix4f; import net.minecraft.resources.ResourceLocation; @@ -28,11 +29,14 @@ public abstract class GlProgram extends GlObject { } public void bind() { - glUseProgram(handle()); + int handle = handle(); + ProgramManager.glUseProgram(handle); + ShaderInstanceAccessor.setLastProgramId(handle); } public void unbind() { - glUseProgram(0); + ProgramManager.glUseProgram(0); + ShaderInstanceAccessor.setLastProgramId(0); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java index 34f4b71d6..db70ceea9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java @@ -35,18 +35,29 @@ public class BatchedMaterialGroup implements MaterialGroup { VertexConsumer buffer = source.getBuffer(state); if (buffer instanceof DirectBufferBuilder direct) { - DirectVertexConsumer consumer = direct.intoDirectConsumer(calculateNeededVertices()); - FormatContext context = new FormatContext(consumer.hasOverlay()); - - for (BatchedMaterial material : materials.values()) { - for (CPUInstancer instancer : material.models.values()) { - instancer.setup(context); - - instancer.submitTasks(stack, pool, consumer); - } - } + renderParallel(stack, pool, direct); } else { - renderInto(stack, buffer, FormatContext.defaultContext()); + renderSerial(stack, buffer, FormatContext.defaultContext()); + } + } + + private void renderParallel(PoseStack stack, Executor pool, DirectBufferBuilder direct) { + int vertexCount = calculateNeededVertices(); + DirectVertexConsumer consumer = direct.intoDirectConsumer(vertexCount); + FormatContext context = new FormatContext(consumer.hasOverlay()); + + for (BatchedMaterial material : materials.values()) { + for (CPUInstancer instancer : material.models.values()) { + instancer.setup(context); + + instancer.submitTasks(stack, pool, consumer); + } + } + } + + private void renderSerial(PoseStack stack, VertexConsumer consumer, FormatContext context) { + for (BatchedMaterial value : materials.values()) { + value.render(stack, consumer, context); } } @@ -61,12 +72,6 @@ public class BatchedMaterialGroup implements MaterialGroup { return total; } - private void renderInto(PoseStack stack, VertexConsumer consumer, FormatContext context) { - for (BatchedMaterial value : materials.values()) { - value.render(stack, consumer, context); - } - } - public void clear() { materials.values().forEach(BatchedMaterial::clear); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index cc5621418..3a16fc17f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -65,6 +65,8 @@ public class BatchingEngine implements Engine { } stack.popPose(); + + event.buffers.bufferSource().endBatch(); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 4231c2759..1f59b3ff3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -8,7 +8,7 @@ import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.core.model.SuperByteBuffer; +import com.jozufozu.flywheel.core.model.ModelTransformer; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -16,14 +16,14 @@ public class CPUInstancer extends AbstractInstancer { private final BatchingTransformer transform; - private final SuperByteBuffer sbb; - private final SuperByteBuffer.Params defaultParams; + private final ModelTransformer sbb; + private final ModelTransformer.Params defaultParams; public CPUInstancer(StructType type, Model modelData) { super(type, modelData); - sbb = new SuperByteBuffer(modelData); - defaultParams = SuperByteBuffer.Params.defaultParams(); + sbb = new ModelTransformer(modelData); + defaultParams = ModelTransformer.Params.defaultParams(); transform = type.asBatched() .getTransformer(); @@ -37,7 +37,7 @@ public class CPUInstancer extends AbstractInstancer { while (instances > 0) { int end = instances; - instances -= 100; + instances -= 512; int start = Math.max(instances, 0); int verts = getModelVertexCount() * (end - start); @@ -54,7 +54,7 @@ public class CPUInstancer extends AbstractInstancer { } private void drawRange(PoseStack stack, VertexConsumer buffer, int from, int to) { - SuperByteBuffer.Params params = defaultParams.copy(); + ModelTransformer.Params params = defaultParams.copy(); for (D d : data.subList(from, to)) { transform.transform(d, params); @@ -66,7 +66,7 @@ public class CPUInstancer extends AbstractInstancer { } void drawAll(PoseStack stack, VertexConsumer buffer) { - SuperByteBuffer.Params params = defaultParams.copy(); + ModelTransformer.Params params = defaultParams.copy(); for (D d : data) { transform.transform(d, params); @@ -77,18 +77,15 @@ public class CPUInstancer extends AbstractInstancer { } void setup(FormatContext context) { - renderSetup(); - - if (context.usesOverlay()) { - defaultParams.entityMode(); - } - } - - protected void renderSetup() { if (anyToRemove) { removeDeletedInstances(); + anyToRemove = false; } - anyToRemove = false; + + if (context.usesOverlay()) { + defaultParams.overlay(); + } } + } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java index e0516e69c..0286083f6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.backend.model; +import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; @@ -22,8 +23,9 @@ public class DirectVertexConsumer implements VertexConsumer { private int uv2 = -1; private long vertexBase; + private final long end; - public DirectVertexConsumer(ByteBuffer buffer, VertexFormat format) { + public DirectVertexConsumer(ByteBuffer buffer, VertexFormat format, int maxVertices) { this.format = format; startPos = buffer.position(); stride = format.getVertexSize(); @@ -48,9 +50,10 @@ public class DirectVertexConsumer implements VertexConsumer { } this.vertexBase = MemoryUtil.memAddress(buffer, startPos); + this.end = vertexBase + (long) maxVertices * stride; } - private DirectVertexConsumer(DirectVertexConsumer parent) { + private DirectVertexConsumer(DirectVertexConsumer parent, int maxVertices) { this.format = parent.format; this.stride = parent.stride; this.startPos = parent.startPos; @@ -62,6 +65,7 @@ public class DirectVertexConsumer implements VertexConsumer { this.uv2 = parent.uv2; this.vertexBase = parent.vertexBase; + this.end = parent.vertexBase + (long) maxVertices * this.stride; } public boolean hasOverlay() { @@ -76,7 +80,7 @@ public class DirectVertexConsumer implements VertexConsumer { public DirectVertexConsumer split(int vertexCount) { int bytes = vertexCount * stride; - DirectVertexConsumer head = new DirectVertexConsumer(this); + DirectVertexConsumer head = new DirectVertexConsumer(this, vertexCount); this.vertexBase += bytes; @@ -85,6 +89,7 @@ public class DirectVertexConsumer implements VertexConsumer { @Override public VertexConsumer vertex(double x, double y, double z) { + checkOverflow(); if (position < 0) return this; long base = vertexBase + position; MemoryUtil.memPutFloat(base, (float) x); @@ -97,6 +102,8 @@ public class DirectVertexConsumer implements VertexConsumer { public VertexConsumer color(int r, int g, int b, int a) { if (color < 0) return this; long base = vertexBase + color; + int color = ((r & 0xFF)) | ((g & 0xFF) << 8) | ((b & 0xFF) << 16) | ((a & 0xFF) << 24); + //MemoryUtil.memPutInt(base, color); MemoryUtil.memPutByte(base, (byte) r); MemoryUtil.memPutByte(base + 1, (byte) g); MemoryUtil.memPutByte(base + 2, (byte) b); @@ -155,4 +162,10 @@ public class DirectVertexConsumer implements VertexConsumer { public void unsetDefaultColor() { } + + private void checkOverflow() { + if (vertexBase >= end) { + throw new BufferOverflowException(); + } + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelData.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelData.java index f8ca670bd..e0965ba4b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelData.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelData.java @@ -1,11 +1,7 @@ package com.jozufozu.flywheel.core.materials.model; import com.jozufozu.flywheel.core.materials.BasicData; -import com.jozufozu.flywheel.core.model.SuperByteBuffer; -import com.jozufozu.flywheel.util.transform.Rotate; -import com.jozufozu.flywheel.util.transform.Scale; import com.jozufozu.flywheel.util.transform.Transform; -import com.jozufozu.flywheel.util.transform.Translate; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; diff --git a/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java similarity index 65% rename from src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java rename to src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java index daf2f3440..eb8176257 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/SuperByteBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java @@ -6,28 +6,19 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.*; -import it.unimi.dsi.fastutil.longs.Long2IntMap; -import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.texture.OverlayTexture; -import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; -import net.minecraft.world.level.Level; import net.minecraftforge.client.model.pipeline.LightUtil; -public class SuperByteBuffer { +public class ModelTransformer { private final Model model; - private final ModelReader template; + private final ModelReader reader; - // Temporary - private static final Long2IntMap WORLD_LIGHT_CACHE = new Long2IntOpenHashMap(); - - public SuperByteBuffer(Model model) { + public ModelTransformer(Model model) { this.model = model; - template = model.getReader(); + reader = model.getReader(); } public void renderInto(Params params, PoseStack input, VertexConsumer builder) { @@ -36,7 +27,6 @@ public class SuperByteBuffer { Vector4f pos = new Vector4f(); Vector3f normal = new Vector3f(); - Vector4f lightPos = new Vector4f(); Matrix4f modelMat = input.last() .pose() @@ -52,23 +42,18 @@ public class SuperByteBuffer { normalMat = params.normal.copy(); } - if (params.useWorldLight) { - WORLD_LIGHT_CACHE.clear(); - } - - float f = .5f; - int vertexCount = template.getVertexCount(); + int vertexCount = reader.getVertexCount(); for (int i = 0; i < vertexCount; i++) { - float x = template.getX(i); - float y = template.getY(i); - float z = template.getZ(i); + float x = reader.getX(i); + float y = reader.getY(i); + float z = reader.getZ(i); pos.set(x, y, z, 1F); pos.transform(modelMat); builder.vertex(pos.x(), pos.y(), pos.z()); - float normalX = template.getNX(i); - float normalY = template.getNY(i); - float normalZ = template.getNZ(i); + float normalX = reader.getNX(i); + float normalY = reader.getNY(i); + float normalZ = reader.getNZ(i); normal.set(normalX, normalY, normalZ); normal.transform(normalMat); @@ -80,13 +65,13 @@ public class SuperByteBuffer { float instanceDiffuse = LightUtil.diffuseLight(nx, ny, nz); switch (params.colorMode) { - case MODEL_ONLY -> builder.color(template.getR(i), template.getG(i), template.getB(i), template.getA(i)); + case MODEL_ONLY -> builder.color(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i)); case DIFFUSE_ONLY -> builder.color(instanceDiffuse, instanceDiffuse, instanceDiffuse, 1f); case MODEL_DIFFUSE -> { - int r = Byte.toUnsignedInt(template.getR(i)); - int g = Byte.toUnsignedInt(template.getG(i)); - int b = Byte.toUnsignedInt(template.getB(i)); - int a = Byte.toUnsignedInt(template.getA(i)); + int r = Byte.toUnsignedInt(reader.getR(i)); + int g = Byte.toUnsignedInt(reader.getG(i)); + int b = Byte.toUnsignedInt(reader.getB(i)); + int a = Byte.toUnsignedInt(reader.getA(i)); float diffuse = switch (params.diffuseMode) { case NONE -> 1f; @@ -118,8 +103,8 @@ public class SuperByteBuffer { //builder.color(Math.max(0, (int) (nx * 255)), Math.max(0, (int) (ny * 255)), Math.max(0, (int) (nz * 255)), 0xFF); //builder.color(Math.max(0, (int) (normalX * 255)), Math.max(0, (int) (normalY * 255)), Math.max(0, (int) (normalZ * 255)), 0xFF); - float u = template.getU(i); - float v = template.getV(i); + float u = reader.getU(i); + float v = reader.getV(i); if (params.spriteShiftFunc != null) { params.spriteShiftFunc.shift(builder, u, v); } else { @@ -130,29 +115,7 @@ public class SuperByteBuffer { builder.overlayCoords(params.overlay); } - int light; - if (params.useWorldLight) { - lightPos.set(((x - f) * 15 / 16f) + f, (y - f) * 15 / 16f + f, (z - f) * 15 / 16f + f, 1F); - lightPos.transform(params.model); - if (params.lightTransform != null) { - lightPos.transform(params.lightTransform); - } - - light = getLight(Minecraft.getInstance().level, lightPos); - if (params.hasCustomLight) { - light = maxLight(light, params.packedLightCoords); - } - } else if (params.hasCustomLight) { - light = params.packedLightCoords; - } else { - light = template.getLight(i); - } - - if (params.hybridLight) { - builder.uv2(maxLight(light, template.getLight(i))); - } else { - builder.uv2(light); - } + builder.uv2(params.hasCustomLight ? params.packedLightCoords : reader.getLight(i)); builder.normal(nx, ny, nz); @@ -161,7 +124,7 @@ public class SuperByteBuffer { } public boolean isEmpty() { - return template.isEmpty(); + return reader.isEmpty(); } @Override @@ -173,19 +136,6 @@ public class SuperByteBuffer { return Mth.clamp((int) (component * scale), 0, 255); } - public static int maxLight(int packedLight1, int packedLight2) { - int blockLight1 = LightTexture.block(packedLight1); - int skyLight1 = LightTexture.sky(packedLight1); - int blockLight2 = LightTexture.block(packedLight2); - int skyLight2 = LightTexture.sky(packedLight2); - return LightTexture.pack(Math.max(blockLight1, blockLight2), Math.max(skyLight1, skyLight2)); - } - - private static int getLight(Level world, Vector4f lightPos) { - BlockPos pos = new BlockPos(lightPos.x(), lightPos.y(), lightPos.z()); - return WORLD_LIGHT_CACHE.computeIfAbsent(pos.asLong(), $ -> LevelRenderer.getLightColor(world, pos)); - } - @FunctionalInterface public interface SpriteShiftFunc { void shift(VertexConsumer builder, float u, float v); @@ -207,12 +157,12 @@ public class SuperByteBuffer { public static class Params implements Transform { // Vertex Position - public final Matrix4f model = new Matrix4f(); - public final Matrix3f normal = new Matrix3f(); + public final Matrix4f model; + public final Matrix3f normal; // Vertex Coloring - public ColorMode colorMode = ColorMode.DIFFUSE_ONLY; - public DiffuseMode diffuseMode = DiffuseMode.INSTANCE; + public ColorMode colorMode; + public DiffuseMode diffuseMode; public int r; public int g; public int b; @@ -223,18 +173,20 @@ public class SuperByteBuffer { // Vertex Overlay Color public boolean hasOverlay; - public int overlay = OverlayTexture.NO_OVERLAY; + public int overlay; // Vertex Lighting - public boolean useWorldLight; - public Matrix4f lightTransform; public boolean hasCustomLight; public int packedLightCoords; - public boolean hybridLight; // Vertex Normals public boolean fullNormalTransform; + public Params() { + model = new Matrix4f(); + normal = new Matrix3f(); + } + public void load(Params from) { model.load(from.model); normal.load(from.normal); @@ -247,11 +199,8 @@ public class SuperByteBuffer { spriteShiftFunc = from.spriteShiftFunc; hasOverlay = from.hasOverlay; overlay = from.overlay; - useWorldLight = from.useWorldLight; - lightTransform = from.lightTransform; hasCustomLight = from.hasCustomLight; packedLightCoords = from.packedLightCoords; - hybridLight = from.hybridLight; fullNormalTransform = from.fullNormalTransform; } @@ -307,23 +256,11 @@ public class SuperByteBuffer { /** * Transforms normals not only by the local matrix stack, but also by the passed matrix stack. */ - public Params entityMode() { + public void entityMode() { this.hasOverlay = true; this.fullNormalTransform = true; this.diffuseMode = DiffuseMode.NONE; this.colorMode = ColorMode.RECOLOR; - return this; - } - - public Params light() { - this.useWorldLight = true; - return this; - } - - public Params light(Matrix4f lightTransform) { - this.useWorldLight = true; - this.lightTransform = lightTransform; - return this; } public Params light(int packedLightCoords) { @@ -332,24 +269,10 @@ public class SuperByteBuffer { return this; } - public Params light(Matrix4f lightTransform, int packedLightCoords) { - light(lightTransform); - light(packedLightCoords); - return this; - } - - /** - * Uses max light from calculated light (world light or custom light) and vertex light for the final light value. - * Ineffective if any other light method was not called. - */ - public Params hybridLight() { - hybridLight = true; - return this; - } - @Override public Params multiply(Quaternion quaternion) { model.multiply(quaternion); + normal.mul(quaternion); return this; } @@ -404,11 +327,8 @@ public class SuperByteBuffer { out.spriteShiftFunc = null; out.hasOverlay = false; out.overlay = OverlayTexture.NO_OVERLAY; - out.useWorldLight = false; - out.lightTransform = null; out.hasCustomLight = false; - out.packedLightCoords = 0; - out.hybridLight = false; + out.packedLightCoords = LightTexture.FULL_BRIGHT; out.fullNormalTransform = false; return out; } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java index 95d2874e3..76dd7f129 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java @@ -42,7 +42,7 @@ public abstract class BufferBuilderMixin implements DirectBufferBuilder { int bytes = vertexCount * format.getVertexSize(); ensureCapacity(bytes); - DirectVertexConsumer consumer = new DirectVertexConsumer(this.buffer, this.format); + DirectVertexConsumer consumer = new DirectVertexConsumer(this.buffer, this.format, vertexCount); this.vertices += vertexCount; this.currentElement = format.getElements() diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java index f0c0d2851..9fd956b00 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java @@ -60,10 +60,6 @@ public class RenderHooksMixin { RenderBuffers renderBuffers = this.renderBuffers; MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(level, type, stack, renderBuffers, camX, camY, camZ)); - - if (!OptifineHandler.usingShaders()) GL20.glUseProgram(0); - - renderBuffers.bufferSource().endBatch(type); } @Inject(at = @At("TAIL"), method = "allChanged") @@ -84,8 +80,6 @@ public class RenderHooksMixin { Vec3 cameraPos = info.getPosition(); CrumblingRenderer.renderBreaking(new RenderLayerEvent(level, null, stack, null, cameraPos.x, cameraPos.y, cameraPos.z)); - - if (!OptifineHandler.usingShaders()) GL20.glUseProgram(0); } // Instancing diff --git a/src/main/java/com/jozufozu/flywheel/mixin/ShaderInstanceAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/ShaderInstanceAccessor.java new file mode 100644 index 000000000..ca5743441 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/ShaderInstanceAccessor.java @@ -0,0 +1,14 @@ +package com.jozufozu.flywheel.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.renderer.ShaderInstance; + +@Mixin(ShaderInstance.class) +public interface ShaderInstanceAccessor { + @Accessor("lastProgramId") + static void setLastProgramId(int id) { + throw new AssertionError(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/BakedQuadWrapper.java b/src/main/java/com/jozufozu/flywheel/util/BakedQuadWrapper.java deleted file mode 100644 index d64915050..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/BakedQuadWrapper.java +++ /dev/null @@ -1,219 +0,0 @@ -package com.jozufozu.flywheel.util; - -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.VertexFormat; -import com.mojang.blaze3d.vertex.VertexFormatElement; -import com.mojang.math.Vector3f; - -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.world.phys.Vec2; - -public class BakedQuadWrapper { - private final FormatCache formatCache = new FormatCache(); - private BakedQuad quad; - private int[] vertexData; - - public BakedQuadWrapper() { - } - - public BakedQuadWrapper(BakedQuad quad) { - this.quad = quad; - this.vertexData = quad.getVertices(); - } - - public void setQuad(BakedQuad quad) { - this.quad = quad; - this.vertexData = this.quad.getVertices(); - } - - public static BakedQuadWrapper of(BakedQuad quad) { - return new BakedQuadWrapper(quad); - } - - public void refreshFormat() { - formatCache.refresh(); - } - - public BakedQuad getQuad() { - return quad; - } - - public void clear() { - quad = null; - vertexData = null; - } - - // Getters - - public float getPosX(int vertexIndex) { - return Float.intBitsToFloat(vertexData[vertexIndex * formatCache.vertexSize + formatCache.position]); - } - - public float getPosY(int vertexIndex) { - return Float.intBitsToFloat(vertexData[vertexIndex * formatCache.vertexSize + formatCache.position + 1]); - } - - public float getPosZ(int vertexIndex) { - return Float.intBitsToFloat(vertexData[vertexIndex * formatCache.vertexSize + formatCache.position + 2]); - } - - public Vector3f getPos(int vertexIndex) { - return new Vector3f(getPosX(vertexIndex), getPosY(vertexIndex), getPosZ(vertexIndex)); - } - - public void copyPos(int vertexIndex, Vector3f pos) { - pos.set(getPosX(vertexIndex), getPosY(vertexIndex), getPosZ(vertexIndex)); - } - - public int getColor(int vertexIndex) { - return vertexData[vertexIndex * formatCache.vertexSize + formatCache.color]; - } - - public float getTexU(int vertexIndex) { - return Float.intBitsToFloat(vertexData[vertexIndex * formatCache.vertexSize + formatCache.texture]); - } - - public float getTexV(int vertexIndex) { - return Float.intBitsToFloat(vertexData[vertexIndex * formatCache.vertexSize + formatCache.texture + 1]); - } - - public Vec2 getTex(int vertexIndex) { - return new Vec2(getTexU(vertexIndex), getTexV(vertexIndex)); - } - - public int getLight(int vertexIndex) { - return vertexData[vertexIndex * formatCache.vertexSize + formatCache.light]; - } - - public float getNormalX(int vertexIndex) { - return Float.intBitsToFloat(vertexData[vertexIndex * formatCache.vertexSize + formatCache.normal]); - } - - public float getNormalY(int vertexIndex) { - return Float.intBitsToFloat(vertexData[vertexIndex * formatCache.vertexSize + formatCache.normal + 1]); - } - - public float getNormalZ(int vertexIndex) { - return Float.intBitsToFloat(vertexData[vertexIndex * formatCache.vertexSize + formatCache.normal + 2]); - } - - public Vector3f getNormal(int vertexIndex) { - return new Vector3f(getNormalX(vertexIndex), getNormalY(vertexIndex), getNormalZ(vertexIndex)); - } - - public void copyNormal(int vertexIndex, Vector3f normal) { - normal.set(getNormalX(vertexIndex), getNormalY(vertexIndex), getNormalZ(vertexIndex)); - } - - // Setters - - public void setPosX(int vertexIndex, float x) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.position] = Float.floatToRawIntBits(x); - } - - public void setPosY(int vertexIndex, float y) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.position + 1] = Float.floatToRawIntBits(y); - } - - public void setPosZ(int vertexIndex, float z) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.position + 2] = Float.floatToRawIntBits(z); - } - - public void setPos(int vertexIndex, float x, float y, float z) { - setPosX(vertexIndex, x); - setPosY(vertexIndex, y); - setPosZ(vertexIndex, z); - } - - public void setPos(int vertexIndex, Vector3f pos) { - setPos(vertexIndex, pos.x(), pos.y(), pos.z()); - } - - public void setColor(int vertexIndex, int color) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.color] = color; - } - - public void setTexU(int vertexIndex, float u) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.texture] = Float.floatToRawIntBits(u); - } - - public void setTexV(int vertexIndex, float v) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.texture + 1] = Float.floatToRawIntBits(v); - } - - public void setTex(int vertexIndex, float u, float v) { - setTexU(vertexIndex, u); - setTexV(vertexIndex, v); - } - - public void setTex(int vertexIndex, Vec2 tex) { - setTex(vertexIndex, tex.x, tex.y); - } - - public void setLight(int vertexIndex, int light) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.light] = light; - } - - public void setNormalX(int vertexIndex, float normalX) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.normal] = Float.floatToRawIntBits(normalX); - } - - public void setNormalY(int vertexIndex, float normalY) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.normal + 1] = Float.floatToRawIntBits(normalY); - } - - public void setNormalZ(int vertexIndex, float normalZ) { - vertexData[vertexIndex * formatCache.vertexSize + formatCache.normal + 2] = Float.floatToRawIntBits(normalZ); - } - - public void setNormal(int vertexIndex, float normalX, float normalY, float normalZ) { - setNormalX(vertexIndex, normalX); - setNormalY(vertexIndex, normalY); - setNormalZ(vertexIndex, normalZ); - } - - public void setNormal(int vertexIndex, Vector3f normal) { - setNormal(vertexIndex, normal.x(), normal.y(), normal.z()); - } - - private static class FormatCache { - private static final VertexFormat FORMAT = DefaultVertexFormat.BLOCK; - - public FormatCache() { - refresh(); - } - - // Integer size - public int vertexSize; - - // Element integer offsets - public int position; - public int color; - public int texture; - public int light; - public int normal; - - public void refresh() { - vertexSize = FORMAT.getIntegerSize(); - for (int elementId = 0; elementId < FORMAT.getElements() - .size(); elementId++) { - VertexFormatElement element = FORMAT.getElements() - .get(elementId); - int intOffset = FORMAT.getOffset(elementId) / Integer.BYTES; - if (element.getUsage() == VertexFormatElement.Usage.POSITION) { - position = intOffset; - } else if (element.getUsage() == VertexFormatElement.Usage.COLOR) { - color = intOffset; - } else if (element.getUsage() == VertexFormatElement.Usage.UV) { - if (element.getIndex() == 0) { - texture = intOffset; - } else if (element.getIndex() == 2) { - light = intOffset; - } - } else if (element.getUsage() == VertexFormatElement.Usage.NORMAL) { - normal = intOffset; - } - } - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/util/ChunkUtil.java b/src/main/java/com/jozufozu/flywheel/util/ChunkUtil.java deleted file mode 100644 index e6612b054..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/ChunkUtil.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.jozufozu.flywheel.util; - -import javax.annotation.Nullable; - -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; - -public class ChunkUtil { - - public static boolean isValidSection(@Nullable LevelChunk chunk, int sectionY) { - if (chunk == null) return false; - - // TODO: 1.17 - LevelChunkSection[] sections = chunk.getSections(); - - return sectionY >= 0 && sectionY < sections.length; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/util/transform/Transform.java b/src/main/java/com/jozufozu/flywheel/util/transform/Transform.java index f74ad22b7..f2ebe795f 100644 --- a/src/main/java/com/jozufozu/flywheel/util/transform/Transform.java +++ b/src/main/java/com/jozufozu/flywheel/util/transform/Transform.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.util.transform; -import com.jozufozu.flywheel.core.model.SuperByteBuffer; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index 7bdf36429..c2e308cef 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -16,6 +16,7 @@ "RenderHooksMixin", "RenderTexturesMixin", "ShaderCloseMixin", + "ShaderInstanceAccessor", "atlas.AtlasDataMixin", "atlas.SheetDataAccessor", "light.LightUpdateMixin", From f651676dea5c739d02a4a0d7aeb2b06bb69d1c49 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 17 Dec 2021 01:03:52 -0800 Subject: [PATCH 05/22] Proper task engine - Manual threadpool - More control --- .../backend/instancing/BatchExecutor.java | 172 ++++++++++++++++++ .../backend/instancing/ImmediateExecutor.java | 21 +++ .../backend/instancing/InstanceWorld.java | 10 +- .../backend/instancing/RenderDispatcher.java | 4 +- .../backend/instancing/TaskEngine.java | 12 ++ .../instancing/batching/BatchExecutor.java | 34 ---- .../batching/BatchedMaterialGroup.java | 6 +- .../instancing/batching/BatchingEngine.java | 27 ++- .../instancing/batching/CPUInstancer.java | 8 +- .../instancing/InstancingEngine.java | 16 +- .../flywheel/core/model/ModelTransformer.java | 1 - 11 files changed, 245 insertions(+), 66 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/ImmediateExecutor.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/TaskEngine.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchExecutor.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java new file mode 100644 index 000000000..f3da663c0 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java @@ -0,0 +1,172 @@ +package com.jozufozu.flywheel.backend.instancing; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.jozufozu.flywheel.backend.instancing.batching.WaitGroup; + +import net.minecraft.util.Mth; + +// https://github.com/CaffeineMC/sodium-fabric/blob/5d364ed5ba63f9067fcf72a078ca310bff4db3e9/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuilder.java +public class BatchExecutor implements TaskEngine { + private static final Logger LOGGER = LogManager.getLogger("BatchExecutor"); + + private final AtomicBoolean running = new AtomicBoolean(false); + private final WaitGroup wg = new WaitGroup(); + + private final Deque jobQueue = new ConcurrentLinkedDeque<>(); + private final List threads = new ArrayList<>(); + + private final Object jobNotifier = new Object(); + + private final int threadCount; + + public BatchExecutor() { + threadCount = getOptimalThreadCount(); + } + + /** + * Spawns a number of work-stealing threads to process results in the build queue. If the builder is already + * running, this method does nothing and exits. + */ + public void startWorkers() { + if (this.running.getAndSet(true)) { + return; + } + + if (!this.threads.isEmpty()) { + throw new IllegalStateException("Threads are still alive while in the STOPPED state"); + } + + for (int i = 0; i < this.threadCount; i++) { + + Thread thread = new Thread(new WorkerRunnable(), "Engine Executor " + i); + thread.setPriority(Math.max(0, Thread.NORM_PRIORITY - 2)); + thread.start(); + + this.threads.add(thread); + } + + LOGGER.info("Started {} worker threads", this.threads.size()); + } + + public void stopWorkers() { + if (!this.running.getAndSet(false)) { + return; + } + + if (this.threads.isEmpty()) { + throw new IllegalStateException("No threads are alive but the executor is in the RUNNING state"); + } + + synchronized (this.jobNotifier) { + this.jobNotifier.notifyAll(); + } + + try { + for (Thread thread : this.threads) { + thread.join(); + } + } catch (InterruptedException ignored) { + } + + this.threads.clear(); + + this.jobQueue.clear(); + } + + /** + * Submit a task to the pool. + */ + @Override + public void submit(@NotNull Runnable command) { + this.jobQueue.add(command); + this.wg.add(1); + + synchronized (this.jobNotifier) { + this.jobNotifier.notify(); + } + } + + /** + * Wait for all running jobs to finish. + */ + @Override + public void syncPoint() { + Runnable job; + + // Finish everyone else's work... + while ((job = getNextTask(false)) != null) { + processTask(job); + } + + // and wait for any stragglers. + try { + this.wg.await(); + } catch (InterruptedException ignored) { + } + } + + @Nullable + private Runnable getNextTask(boolean block) { + Runnable job = this.jobQueue.poll(); + + if (job == null && block) { + synchronized (BatchExecutor.this.jobNotifier) { + try { + BatchExecutor.this.jobNotifier.wait(); + } catch (InterruptedException ignored) { + } + } + } + + return job; + } + + private void processTask(Runnable job) { + try { + job.run(); + } finally { + BatchExecutor.this.wg.done(); + } + } + + /** + * Returns the "optimal" number of threads to be used for chunk build tasks. This will always return at least one + * thread. + */ + private static int getOptimalThreadCount() { + return Mth.clamp(Math.max(getMaxThreadCount() / 3, getMaxThreadCount() - 6), 1, 10); + } + + private static int getMaxThreadCount() { + return Runtime.getRuntime().availableProcessors(); + } + private class WorkerRunnable implements Runnable { + + private final AtomicBoolean running = BatchExecutor.this.running; + + @Override + public void run() { + // Run until the chunk builder shuts down + while (this.running.get()) { + Runnable job = BatchExecutor.this.getNextTask(true); + + if (job == null) { + continue; + } + + processTask(job); + } + } + + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/ImmediateExecutor.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/ImmediateExecutor.java new file mode 100644 index 000000000..be1c2b5fd --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/ImmediateExecutor.java @@ -0,0 +1,21 @@ +package com.jozufozu.flywheel.backend.instancing; + +import org.jetbrains.annotations.NotNull; + +public class ImmediateExecutor implements TaskEngine { + + public static final ImmediateExecutor INSTANCE = new ImmediateExecutor(); + + private ImmediateExecutor() { + } + + @Override + public void submit(@NotNull Runnable command) { + command.run(); + } + + @Override + public void syncPoint() { + // noop + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index f2cd13180..a0050a65f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -29,15 +29,20 @@ public class InstanceWorld { protected final InstanceManager entityInstanceManager; protected final InstanceManager tileEntityInstanceManager; + protected final BatchExecutor executor; + public InstanceWorld() { + this.executor = new BatchExecutor(); + this.executor.startWorkers(); + FlwEngine engine = Backend.getInstance() .getEngine(); switch (engine) { case GL33 -> { InstancingEngine manager = InstancingEngine.builder(Contexts.WORLD) - .build(); + .build(this.executor); entityInstanceManager = new EntityInstanceManager(manager); tileEntityInstanceManager = new TileInstanceManager(manager); @@ -47,7 +52,7 @@ public class InstanceWorld { this.engine = manager; } case BATCHING -> { - this.engine = new BatchingEngine(); + this.engine = new BatchingEngine(this.executor); entityInstanceManager = new EntityInstanceManager(this.engine); tileEntityInstanceManager = new TileInstanceManager(this.engine); } @@ -67,6 +72,7 @@ public class InstanceWorld { * Free all acquired resources and invalidate this instance world. */ public void delete() { + this.executor.stopWorkers(); engine.delete(); entityInstanceManager.detachLightListeners(); tileEntityInstanceManager.detachLightListeners(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java index fd4681dd1..c54d60205 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java @@ -21,7 +21,5 @@ public interface RenderDispatcher { */ void beginFrame(Camera info); - default void delete() { - - } + void delete(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/TaskEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/TaskEngine.java new file mode 100644 index 000000000..76dbeb9c2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/TaskEngine.java @@ -0,0 +1,12 @@ +package com.jozufozu.flywheel.backend.instancing; + +import org.jetbrains.annotations.NotNull; + +public interface TaskEngine { + void submit(@NotNull Runnable command); + + /** + * Wait for all running jobs to finish. + */ + void syncPoint(); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchExecutor.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchExecutor.java deleted file mode 100644 index 60e41f2b4..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchExecutor.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.batching; - -import java.util.concurrent.Executor; - -import org.jetbrains.annotations.NotNull; - -public class BatchExecutor implements Executor { - private final Executor internal; - private final WaitGroup wg; - - public BatchExecutor(Executor internal) { - this.internal = internal; - - wg = new WaitGroup(); - } - - @Override - public void execute(@NotNull Runnable command) { - wg.add(1); - internal.execute(() -> { - // wrapper function to decrement the wait group - try { - command.run(); - } catch (Exception ignored) { - } finally { - wg.done(); - } - }); - } - - public void await() throws InterruptedException { - wg.await(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java index db70ceea9..c78e3abe4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java @@ -2,11 +2,11 @@ package com.jozufozu.flywheel.backend.instancing.batching; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.Executor; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.model.DirectBufferBuilder; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.mojang.blaze3d.vertex.PoseStack; @@ -31,7 +31,7 @@ public class BatchedMaterialGroup implements MaterialGroup { return (BatchedMaterial) materials.computeIfAbsent(spec, BatchedMaterial::new); } - public void render(PoseStack stack, MultiBufferSource source, Executor pool) { + public void render(PoseStack stack, MultiBufferSource source, TaskEngine pool) { VertexConsumer buffer = source.getBuffer(state); if (buffer instanceof DirectBufferBuilder direct) { @@ -41,7 +41,7 @@ public class BatchedMaterialGroup implements MaterialGroup { } } - private void renderParallel(PoseStack stack, Executor pool, DirectBufferBuilder direct) { + private void renderParallel(PoseStack stack, TaskEngine pool, DirectBufferBuilder direct) { int vertexCount = calculateNeededVertices(); DirectVertexConsumer consumer = direct.intoDirectConsumer(vertexCount); FormatContext context = new FormatContext(consumer.hasOverlay()); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index 3a16fc17f..7d8377ae3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -3,11 +3,10 @@ package com.jozufozu.flywheel.backend.instancing.batching; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.backend.RenderLayer; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.event.RenderLayerEvent; import com.mojang.blaze3d.vertex.PoseStack; @@ -20,19 +19,16 @@ import net.minecraft.core.Vec3i; public class BatchingEngine implements Engine { - protected BlockPos originCoordinate = BlockPos.ZERO; - protected final Map> layers; + protected final TaskEngine taskEngine; - private final BatchExecutor pool; - - public BatchingEngine() { + public BatchingEngine(TaskEngine taskEngine) { this.layers = new EnumMap<>(RenderLayer.class); for (RenderLayer value : RenderLayer.values()) { layers.put(value, new HashMap<>()); } - pool = new BatchExecutor(Executors.newWorkStealingPool(ForkJoinPool.getCommonPoolParallelism())); + this.taskEngine = taskEngine; } @Override @@ -42,7 +38,7 @@ public class BatchingEngine implements Engine { @Override public Vec3i getOriginCoordinate() { - return originCoordinate; + return BlockPos.ZERO; } @Override @@ -53,22 +49,25 @@ public class BatchingEngine implements Engine { stack.translate(-event.camX, -event.camY, -event.camZ); + taskEngine.syncPoint(); + for (Map.Entry entry : layers.get(event.getLayer()).entrySet()) { BatchedMaterialGroup group = entry.getValue(); - group.render(stack, buffers, pool); + group.render(stack, buffers, taskEngine); } - try { - pool.await(); - } catch (InterruptedException ignored) { - } + taskEngine.syncPoint(); stack.popPose(); event.buffers.bufferSource().endBatch(); } + @Override + public void delete() { + } + @Override public void beginFrame(Camera info) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 1f59b3ff3..292bdddad 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -1,11 +1,10 @@ package com.jozufozu.flywheel.backend.instancing.batching; -import java.util.concurrent.Executor; - import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.BatchingTransformer; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.ModelTransformer; @@ -32,7 +31,7 @@ public class CPUInstancer extends AbstractInstancer { } } - void submitTasks(PoseStack stack, Executor pool, DirectVertexConsumer consumer) { + void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) { int instances = numInstances(); while (instances > 0) { @@ -44,7 +43,7 @@ public class CPUInstancer extends AbstractInstancer { DirectVertexConsumer sub = consumer.split(verts); - pool.execute(() -> drawRange(stack, sub, start, end)); + pool.submit(() -> drawRange(stack, sub, start, end)); } } @@ -82,7 +81,6 @@ public class CPUInstancer extends AbstractInstancer { anyToRemove = false; } - if (context.usesOverlay()) { defaultParams.overlay(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 1332a4a56..1ddc21fd5 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -9,6 +9,8 @@ import java.util.stream.Stream; import javax.annotation.Nullable; import com.jozufozu.flywheel.api.MaterialGroup; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; +import com.jozufozu.flywheel.backend.instancing.ImmediateExecutor; import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; @@ -33,6 +35,7 @@ public class InstancingEngine

implements Engine { protected BlockPos originCoordinate = BlockPos.ZERO; + protected final TaskEngine taskEngine; protected final WorldContext

context; protected final GroupFactory

groupFactory; protected final boolean ignoreOriginCoordinate; @@ -41,15 +44,16 @@ public class InstancingEngine

implements Engine { private final WeakHashSet listeners; - public InstancingEngine(WorldContext

context) { - this(context, InstancedMaterialGroup::new, false); + public InstancingEngine(WorldContext

context, TaskEngine taskEngine) { + this(taskEngine, context, InstancedMaterialGroup::new, false); } public static

Builder

builder(WorldContext

context) { return new Builder<>(context); } - public InstancingEngine(WorldContext

context, GroupFactory

groupFactory, boolean ignoreOriginCoordinate) { + public InstancingEngine(TaskEngine taskEngine, WorldContext

context, GroupFactory

groupFactory, boolean ignoreOriginCoordinate) { + this.taskEngine = taskEngine; this.context = context; this.ignoreOriginCoordinate = ignoreOriginCoordinate; @@ -200,7 +204,11 @@ public class InstancingEngine

implements Engine { } public InstancingEngine

build() { - return new InstancingEngine<>(context, groupFactory, ignoreOriginCoordinate); + return build(ImmediateExecutor.INSTANCE); + } + + public InstancingEngine

build(TaskEngine taskEngine) { + return new InstancingEngine<>(taskEngine, context, groupFactory, ignoreOriginCoordinate); } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java index eb8176257..4a8b6bf72 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java @@ -34,7 +34,6 @@ public class ModelTransformer { modelMat.multiply(params.model); Matrix3f normalMat; - if (params.fullNormalTransform) { normalMat = input.last().normal().copy(); normalMat.mul(params.normal); From ffe17e4449c0a02b894e76f42727e3efed2fd1ae Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 17 Dec 2021 02:17:39 -0800 Subject: [PATCH 06/22] Use task engine/sync to update instances --- .../backend/instancing/InstanceManager.java | 71 +++++++++++++------ .../backend/instancing/InstanceWorld.java | 23 +++--- .../instancing/batching/BatchingEngine.java | 2 - .../entity/EntityInstanceManager.java | 6 +- .../instancing/tile/TileInstanceManager.java | 6 +- .../crumbling/CrumblingInstanceManager.java | 3 +- 6 files changed, 71 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 14c51f360..758168b9b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -31,11 +32,13 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift protected final Map instances; protected final Object2ObjectOpenHashMap tickableInstances; protected final Object2ObjectOpenHashMap dynamicInstances; + private final TaskEngine taskEngine; protected int frame; protected int tick; - public InstanceManager(MaterialManager materialManager) { + public InstanceManager(TaskEngine taskEngine, MaterialManager materialManager) { + this.taskEngine = taskEngine; this.materialManager = materialManager; this.queuedUpdates = new HashSet<>(64); this.queuedAdditions = new HashSet<>(64); @@ -84,25 +87,39 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift int cY = (int) cameraY; int cZ = (int) cameraZ; - if (tickableInstances.size() > 0) { - tickableInstances.object2ObjectEntrySet().parallelStream().forEach(e -> { - ITickableInstance instance = e.getValue(); - if (!instance.decreaseTickRateWithDistance()) { - instance.tick(); - return; + ArrayList instances = new ArrayList<>(tickableInstances.values()); + int incr = 500; + int size = instances.size(); + int start = 0; + while (start < size) { + int end = Math.min(start + incr, size); + + List sub = instances.subList(start, end); + taskEngine.submit(() -> { + for (ITickableInstance instance : sub) { + tickInstance(cX, cY, cZ, instance); } - - BlockPos pos = instance.getWorldPosition(); - - int dX = pos.getX() - cX; - int dY = pos.getY() - cY; - int dZ = pos.getZ() - cZ; - - if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick(); }); + + start += incr; } } + private void tickInstance(int cX, int cY, int cZ, ITickableInstance instance) { + if (!instance.decreaseTickRateWithDistance()) { + instance.tick(); + return; + } + + BlockPos pos = instance.getWorldPosition(); + + int dX = pos.getX() - cX; + int dY = pos.getY() - cY; + int dZ = pos.getZ() - cZ; + + if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick(); + } + public void beginFrame(Camera info) { frame++; processQueuedAdditions(); @@ -117,14 +134,22 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift int cY = (int) info.getPosition().y; int cZ = (int) info.getPosition().z; - if (dynamicInstances.size() > 0) { - dynamicInstances.object2ObjectEntrySet() - .parallelStream() - .forEach(e -> { - IDynamicInstance dyn = e.getValue(); - if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ)) - dyn.beginFrame(); - }); + ArrayList instances = new ArrayList<>(dynamicInstances.values()); + int incr = 500; + int size = instances.size(); + int start = 0; + while (start < size) { + int end = Math.min(start + incr, size); + + List sub = instances.subList(start, end); + taskEngine.submit(() -> { + for (IDynamicInstance dyn : sub) { + if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ)) + dyn.beginFrame(); + } + }); + + start += incr; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index a0050a65f..10862c154 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -29,12 +29,12 @@ public class InstanceWorld { protected final InstanceManager entityInstanceManager; protected final InstanceManager tileEntityInstanceManager; - protected final BatchExecutor executor; + protected final BatchExecutor taskEngine; public InstanceWorld() { - this.executor = new BatchExecutor(); - this.executor.startWorkers(); + this.taskEngine = new BatchExecutor(); + this.taskEngine.startWorkers(); FlwEngine engine = Backend.getInstance() .getEngine(); @@ -42,19 +42,19 @@ public class InstanceWorld { switch (engine) { case GL33 -> { InstancingEngine manager = InstancingEngine.builder(Contexts.WORLD) - .build(this.executor); + .build(this.taskEngine); - entityInstanceManager = new EntityInstanceManager(manager); - tileEntityInstanceManager = new TileInstanceManager(manager); + entityInstanceManager = new EntityInstanceManager(this.taskEngine, manager); + tileEntityInstanceManager = new TileInstanceManager(this.taskEngine, manager); manager.addListener(entityInstanceManager); manager.addListener(tileEntityInstanceManager); this.engine = manager; } case BATCHING -> { - this.engine = new BatchingEngine(this.executor); - entityInstanceManager = new EntityInstanceManager(this.engine); - tileEntityInstanceManager = new TileInstanceManager(this.engine); + this.engine = new BatchingEngine(this.taskEngine); + entityInstanceManager = new EntityInstanceManager(this.taskEngine, this.engine); + tileEntityInstanceManager = new TileInstanceManager(this.taskEngine, this.engine); } default -> throw new IllegalArgumentException("Unknown engine type"); } @@ -72,7 +72,7 @@ public class InstanceWorld { * Free all acquired resources and invalidate this instance world. */ public void delete() { - this.executor.stopWorkers(); + this.taskEngine.stopWorkers(); engine.delete(); entityInstanceManager.detachLightListeners(); tileEntityInstanceManager.detachLightListeners(); @@ -89,6 +89,8 @@ public class InstanceWorld { public void beginFrame(BeginFrameEvent event) { engine.beginFrame(event.getInfo()); + taskEngine.syncPoint(); + tileEntityInstanceManager.beginFrame(event.getInfo()); entityInstanceManager.beginFrame(event.getInfo()); } @@ -113,6 +115,7 @@ public class InstanceWorld { * Draw the given layer. */ public void renderLayer(RenderLayerEvent event) { + taskEngine.syncPoint(); engine.render(event, event.buffers.bufferSource()); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index 7d8377ae3..e1dc909af 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -49,8 +49,6 @@ public class BatchingEngine implements Engine { stack.translate(-event.camX, -event.camY, -event.camZ); - taskEngine.syncPoint(); - for (Map.Entry entry : layers.get(event.getLayer()).entrySet()) { BatchedMaterialGroup group = entry.getValue(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstanceManager.java index 223f725fb..2b3af7808 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstanceManager.java @@ -3,8 +3,10 @@ package com.jozufozu.flywheel.backend.instancing.entity; import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.AbstractInstance; +import com.jozufozu.flywheel.backend.instancing.BatchExecutor; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; @@ -13,8 +15,8 @@ import net.minecraft.world.level.Level; public class EntityInstanceManager extends InstanceManager { - public EntityInstanceManager(MaterialManager materialManager) { - super(materialManager); + public EntityInstanceManager(TaskEngine executor, MaterialManager materialManager) { + super(executor, materialManager); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileInstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileInstanceManager.java index aa4e6c274..c0b65215d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileInstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileInstanceManager.java @@ -3,8 +3,10 @@ package com.jozufozu.flywheel.backend.instancing.tile; import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.AbstractInstance; +import com.jozufozu.flywheel.backend.instancing.BatchExecutor; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; import net.minecraft.core.BlockPos; import net.minecraft.world.level.BlockGetter; @@ -13,8 +15,8 @@ import net.minecraft.world.level.block.entity.BlockEntity; public class TileInstanceManager extends InstanceManager { - public TileInstanceManager(MaterialManager materialManager) { - super(materialManager); + public TileInstanceManager(TaskEngine executor, MaterialManager materialManager) { + super(executor, materialManager); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java index 30e427f6b..bea324fd4 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.core.crumbling; import com.jozufozu.flywheel.api.MaterialManager; +import com.jozufozu.flywheel.backend.instancing.ImmediateExecutor; import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; import net.minecraft.core.BlockPos; @@ -8,7 +9,7 @@ import net.minecraft.core.BlockPos; public class CrumblingInstanceManager extends TileInstanceManager { public CrumblingInstanceManager(MaterialManager materialManager) { - super(materialManager); + super(ImmediateExecutor.INSTANCE, materialManager); } @Override From a43fe2bc9a1ae6a140b9aec8b8e159d620858198 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 17 Dec 2021 22:41:22 -0800 Subject: [PATCH 07/22] No need for getDirtyBitSet - Faster and simpler to just do one loop --- .../backend/instancing/AbstractInstancer.java | 13 -------- .../instancing/instancing/GPUInstancer.java | 33 +++++++------------ 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index 06e6071fd..af7005abb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -70,19 +70,6 @@ public abstract class AbstractInstancer implements Insta return getModelVertexCount() * numInstances(); } - protected BitSet getDirtyBitSet() { - final int size = data.size(); - final BitSet dirtySet = new BitSet(size); - - for (int i = 0; i < size; i++) { - D element = data.get(i); - if (element.checkDirtyAndClear()) { - dirtySet.set(i); - } - } - return dirtySet; - } - protected void removeDeletedInstances() { // Figure out which elements are to be removed. final int oldSize = this.data.size(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index 879f35785..fc1480ebf 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -150,31 +150,20 @@ public class GPUInstancer extends AbstractInstancer { if (size <= 0) return; - final int stride = instanceFormat.getStride(); - final BitSet dirtySet = getDirtyBitSet(); + try (MappedBuffer mapped = instanceVBO.getBuffer(0, glBufferSize)) { - if (dirtySet.isEmpty()) return; + final StructWriter writer = type.asInstanced() + .getWriter(mapped); - final int firstDirty = dirtySet.nextSetBit(0); - final int lastDirty = dirtySet.previousSetBit(size); - - final int offset = firstDirty * stride; - final int length = (1 + lastDirty - firstDirty) * stride; - - if (length > 0) { - try (MappedBuffer mapped = instanceVBO.getBuffer(offset, length)) { - - StructWriter writer = type.asInstanced() - .getWriter(mapped); - - dirtySet.stream() - .forEach(i -> { - writer.seek(i); - writer.write(data.get(i)); - }); - } catch (Exception e) { - Flywheel.log.error("Error updating GPUInstancer:", e); + for (int i = 0; i < size; i++) { + final D element = data.get(i); + if (element.checkDirtyAndClear()) { + writer.seek(i); + writer.write(element); + } } + } catch (Exception e) { + Flywheel.log.error("Error updating GPUInstancer:", e); } } From 4908abc9e08c09cf78b4d584fa04303599623ae4 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 20 Dec 2021 22:38:33 -0800 Subject: [PATCH 08/22] StructType doesn't need #asBatched and #asInstanced - More organized this way - BatchingTransformer function moved into Batched --- .../jozufozu/flywheel/api/struct/Batched.java | 9 +++------ .../api/struct/BatchingTransformer.java | 9 --------- .../flywheel/api/struct/Instanced.java | 4 ---- .../flywheel/api/struct/StructType.java | 3 --- .../backend/instancing/AbstractInstancer.java | 7 ++++--- .../instancing/batching/BatchedMaterial.java | 6 +++--- .../batching/BatchedMaterialGroup.java | 11 ++++++++--- .../instancing/batching/BatchingEngine.java | 4 +--- .../instancing/batching/CPUInstancer.java | 19 +++++++------------ .../instancing/instancing/GPUInstancer.java | 13 +++++++------ .../instancing/InstancedMaterial.java | 8 ++++---- .../instancing/InstancedMaterialGroup.java | 14 +++++++++----- .../instancing/InstancingEngine.java | 1 + .../flywheel/backend/struct/package-info.java | 6 ++++++ .../jozufozu/flywheel/core/WorldContext.java | 7 +++++-- .../core/materials/model/ModelType.java | 12 +++++------- .../core/materials/oriented/OrientedType.java | 16 +++++++--------- 17 files changed, 70 insertions(+), 79 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/struct/package-info.java diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/Batched.java b/src/main/java/com/jozufozu/flywheel/api/struct/Batched.java index a61e326fd..239e088ca 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/Batched.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/Batched.java @@ -1,11 +1,8 @@ package com.jozufozu.flywheel.api.struct; +import com.jozufozu.flywheel.core.model.ModelTransformer; + public interface Batched extends StructType { - BatchingTransformer getTransformer(); - - @Override - default Batched asBatched() { - return this; - } + void transform(S d, ModelTransformer.Params b); } diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java b/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java deleted file mode 100644 index 06ca11419..000000000 --- a/src/main/java/com/jozufozu/flywheel/api/struct/BatchingTransformer.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jozufozu.flywheel.api.struct; - -import com.jozufozu.flywheel.core.model.ModelTransformer; - -@FunctionalInterface -public interface BatchingTransformer { - - void transform(S s, ModelTransformer.Params b); -} diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/Instanced.java b/src/main/java/com/jozufozu/flywheel/api/struct/Instanced.java index f09ca6bff..64e95b6ba 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/Instanced.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/Instanced.java @@ -14,8 +14,4 @@ public interface Instanced extends StructType { ResourceLocation getProgramSpec(); - @Override - default Instanced asInstanced() { - return this; - } } diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java index 999161229..5d5e2985e 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java @@ -18,7 +18,4 @@ public interface StructType { */ VertexFormat format(); - Instanced asInstanced(); - - Batched asBatched(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index af7005abb..a13df5f17 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.ArrayList; import java.util.BitSet; +import java.util.function.Supplier; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.Instancer; @@ -10,13 +11,13 @@ import com.jozufozu.flywheel.core.model.Model; public abstract class AbstractInstancer implements Instancer { - protected final StructType type; + protected final Supplier type; protected final Model modelData; protected final ArrayList data = new ArrayList<>(); protected boolean anyToRemove; - protected AbstractInstancer(StructType type, Model modelData) { + protected AbstractInstancer(Supplier type, Model modelData) { this.type = type; this.modelData = modelData; } @@ -26,7 +27,7 @@ public abstract class AbstractInstancer implements Insta */ @Override public D createInstance() { - return _add(type.create()); + return _add(type.get()); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java index 18183fa6a..1b314a10a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java @@ -7,7 +7,7 @@ import java.util.function.Supplier; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Material; -import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.core.model.Model; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -15,9 +15,9 @@ import com.mojang.blaze3d.vertex.VertexConsumer; public class BatchedMaterial implements Material { protected final Map> models; - private final StructType type; + private final Batched type; - public BatchedMaterial(StructType type) { + public BatchedMaterial(Batched type) { this.type = type; this.models = new HashMap<>(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java index c78e3abe4..1dd50c414 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java @@ -5,6 +5,7 @@ import java.util.Map; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.MaterialGroup; +import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.model.DirectBufferBuilder; @@ -19,7 +20,7 @@ public class BatchedMaterialGroup implements MaterialGroup { protected final RenderType state; - private final Map, BatchedMaterial> materials = new HashMap<>(); + private final Map, BatchedMaterial> materials = new HashMap<>(); public BatchedMaterialGroup(RenderType state) { this.state = state; @@ -27,8 +28,12 @@ public class BatchedMaterialGroup implements MaterialGroup { @SuppressWarnings("unchecked") @Override - public BatchedMaterial material(StructType spec) { - return (BatchedMaterial) materials.computeIfAbsent(spec, BatchedMaterial::new); + public BatchedMaterial material(StructType type) { + if (type instanceof Batched batched) { + return (BatchedMaterial) materials.computeIfAbsent(batched, BatchedMaterial::new); + } else { + throw new ClassCastException("Cannot use type '" + type + "' with CPU instancing."); + } } public void render(PoseStack stack, MultiBufferSource source, TaskEngine pool) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index e1dc909af..1d49062c9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -49,9 +49,7 @@ public class BatchingEngine implements Engine { stack.translate(-event.camX, -event.camY, -event.camZ); - for (Map.Entry entry : layers.get(event.getLayer()).entrySet()) { - BatchedMaterialGroup group = entry.getValue(); - + for (BatchedMaterialGroup group : layers.get(event.getLayer()).values()) { group.render(stack, buffers, taskEngine); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 292bdddad..1c39a076d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -1,7 +1,7 @@ package com.jozufozu.flywheel.backend.instancing.batching; import com.jozufozu.flywheel.api.InstanceData; -import com.jozufozu.flywheel.api.struct.BatchingTransformer; +import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.instancing.TaskEngine; @@ -13,22 +13,17 @@ import com.mojang.blaze3d.vertex.VertexConsumer; public class CPUInstancer extends AbstractInstancer { - private final BatchingTransformer transform; + private final Batched batchingType; private final ModelTransformer sbb; private final ModelTransformer.Params defaultParams; - public CPUInstancer(StructType type, Model modelData) { - super(type, modelData); + public CPUInstancer(Batched type, Model modelData) { + super(type::create, modelData); + batchingType = type; sbb = new ModelTransformer(modelData); defaultParams = ModelTransformer.Params.defaultParams(); - transform = type.asBatched() - .getTransformer(); - - if (transform == null) { - throw new NullPointerException("Cannot batch " + type.toString()); - } } void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) { @@ -56,7 +51,7 @@ public class CPUInstancer extends AbstractInstancer { ModelTransformer.Params params = defaultParams.copy(); for (D d : data.subList(from, to)) { - transform.transform(d, params); + batchingType.transform(d, params); sbb.renderInto(params, stack, buffer); @@ -67,7 +62,7 @@ public class CPUInstancer extends AbstractInstancer { void drawAll(PoseStack stack, VertexConsumer buffer) { ModelTransformer.Params params = defaultParams.copy(); for (D d : data) { - transform.transform(d, params); + batchingType.transform(d, params); sbb.renderInto(params, stack, buffer); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index fc1480ebf..a753b0b05 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -4,6 +4,7 @@ import java.util.BitSet; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.InstanceData; +import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.backend.Backend; @@ -23,6 +24,7 @@ public class GPUInstancer extends AbstractInstancer { private final ModelAllocator modelAllocator; private final VertexFormat instanceFormat; + private final Instanced instancedType; private IBufferedModel model; private GlVertexArray vao; @@ -34,10 +36,11 @@ public class GPUInstancer extends AbstractInstancer { protected boolean anyToUpdate; - public GPUInstancer(StructType type, Model model, ModelAllocator modelAllocator) { - super(type, model); + public GPUInstancer(Instanced type, Model model, ModelAllocator modelAllocator) { + super(type::create, model); this.modelAllocator = modelAllocator; this.instanceFormat = type.format(); + instancedType = type; } @Override @@ -152,8 +155,7 @@ public class GPUInstancer extends AbstractInstancer { try (MappedBuffer mapped = instanceVBO.getBuffer(0, glBufferSize)) { - final StructWriter writer = type.asInstanced() - .getWriter(mapped); + final StructWriter writer = instancedType.getWriter(mapped); for (int i = 0; i < size; i++) { final D element = data.get(i); @@ -176,8 +178,7 @@ public class GPUInstancer extends AbstractInstancer { instanceVBO.alloc(glBufferSize); try (MappedBuffer buffer = instanceVBO.getBuffer(0, glBufferSize)) { - StructWriter writer = type.asInstanced() - .getWriter(buffer); + StructWriter writer = instancedType.getWriter(buffer); for (D datum : data) { writer.write(datum); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java index 24b569b30..a8229424b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java @@ -8,7 +8,7 @@ import com.google.common.cache.CacheBuilder; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Material; -import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.model.ImmediateAllocator; @@ -25,10 +25,10 @@ public class InstancedMaterial implements Material { final ModelAllocator allocator; protected final Cache> models; - protected final StructType type; + protected final Instanced type; - public InstancedMaterial(StructType spec) { - this.type = spec; + public InstancedMaterial(Instanced type) { + this.type = type; if (Backend.getInstance().compat.onAMDWindows()) { allocator = ImmediateAllocator.INSTANCE; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java index 466c82e15..184aa3be4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java @@ -6,6 +6,7 @@ import java.util.Map; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.MaterialGroup; +import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.model.ModelPool; import com.jozufozu.flywheel.core.shader.WorldProgram; @@ -25,7 +26,7 @@ public class InstancedMaterialGroup

implements MaterialG protected final InstancingEngine

owner; protected final RenderType type; - private final Map, InstancedMaterial> materials = new HashMap<>(); + private final Map, InstancedMaterial> materials = new HashMap<>(); public InstancedMaterialGroup(InstancingEngine

owner, RenderType type) { this.owner = owner; @@ -34,8 +35,12 @@ public class InstancedMaterialGroup

implements MaterialG @SuppressWarnings("unchecked") @Override - public InstancedMaterial material(StructType spec) { - return (InstancedMaterial) materials.computeIfAbsent(spec, InstancedMaterial::new); + public InstancedMaterial material(StructType type) { + if (type instanceof Instanced instanced) { + return (InstancedMaterial) materials.computeIfAbsent(instanced, InstancedMaterial::new); + } else { + throw new ClassCastException("Cannot use type '" + type + "' with GPU instancing."); + } } public void render(Matrix4f viewProjection, double camX, double camY, double camZ) { @@ -46,7 +51,7 @@ public class InstancedMaterialGroup

implements MaterialG } protected void renderAll(Matrix4f viewProjection, double camX, double camY, double camZ) { - for (Map.Entry, InstancedMaterial> entry : materials.entrySet()) { + for (Map.Entry, InstancedMaterial> entry : materials.entrySet()) { InstancedMaterial material = entry.getValue(); if (material.nothingToRender()) continue; @@ -61,7 +66,6 @@ public class InstancedMaterialGroup

implements MaterialG } P program = owner.getProgram(entry.getKey() - .asInstanced() .getProgramSpec()).get(); program.bind(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 1ddc21fd5..af045697a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -109,6 +109,7 @@ public class InstancingEngine

implements Engine { } private Stream> getGroupsToRender(@Nullable RenderLayer layer) { + // layer is null when this is called from CrumblingRenderer if (layer != null) { return layers.get(layer) .values() diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/struct/package-info.java new file mode 100644 index 000000000..d4cbaadf2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.struct; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/WorldContext.java b/src/main/java/com/jozufozu/flywheel/core/WorldContext.java index 5271003bd..5306d605c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/WorldContext.java +++ b/src/main/java/com/jozufozu/flywheel/core/WorldContext.java @@ -2,9 +2,11 @@ package com.jozufozu.flywheel.core; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.function.Supplier; import java.util.stream.Stream; +import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.ShaderContext; import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline; @@ -87,8 +89,9 @@ public class WorldContext

implements ShaderContext

{ if (specStream == null) { specStream = () -> backend.allMaterials() .stream() - .map(type -> type.asInstanced() - .getProgramSpec()); + .map(t -> t instanceof Instanced i ? i : null) + .filter(Objects::nonNull) + .map(Instanced::getProgramSpec); } return new WorldContext<>(backend, name, specStream, pipeline); } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java index 9527cbb1f..55bef78a8 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java @@ -1,13 +1,13 @@ package com.jozufozu.flywheel.core.materials.model; import com.jozufozu.flywheel.api.struct.Batched; -import com.jozufozu.flywheel.api.struct.BatchingTransformer; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.Programs; +import com.jozufozu.flywheel.core.model.ModelTransformer; import net.minecraft.resources.ResourceLocation; @@ -34,11 +34,9 @@ public class ModelType implements Instanced, Batched { } @Override - public BatchingTransformer getTransformer() { - return (d, b) -> { - b.transform(d.model, d.normal) - .color(d.r, d.g, d.b, d.a) - .light(d.getPackedLight()); - }; + public void transform(ModelData d, ModelTransformer.Params b) { + b.transform(d.model, d.normal) + .color(d.r, d.g, d.b, d.a) + .light(d.getPackedLight()); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java index c5625bca0..2bd2195ae 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java @@ -1,13 +1,13 @@ package com.jozufozu.flywheel.core.materials.oriented; import com.jozufozu.flywheel.api.struct.Batched; -import com.jozufozu.flywheel.api.struct.BatchingTransformer; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.Programs; +import com.jozufozu.flywheel.core.model.ModelTransformer; import com.mojang.math.Quaternion; import net.minecraft.resources.ResourceLocation; @@ -35,13 +35,11 @@ public class OrientedType implements Instanced, Batched getTransformer() { - return (d, sbb) -> { - sbb.light(d.getPackedLight()) - .color(d.r, d.g, d.b, d.a) - .translate(d.posX + d.pivotX, d.posY + d.pivotY, d.posZ + d.pivotZ) - .multiply(new Quaternion(d.qX, d.qY, d.qZ, d.qW)) - .translate(-d.pivotX, -d.pivotY, -d.pivotZ); - }; + public void transform(OrientedData d, ModelTransformer.Params b) { + b.light(d.getPackedLight()) + .color(d.r, d.g, d.b, d.a) + .translate(d.posX + d.pivotX, d.posY + d.pivotY, d.posZ + d.pivotZ) + .multiply(new Quaternion(d.qX, d.qY, d.qZ, d.qW)) + .translate(-d.pivotX, -d.pivotY, -d.pivotZ); } } From 4f31fa3e9caaedf4307c94f9755bef98a5b65976 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 21 Dec 2021 22:47:29 -0800 Subject: [PATCH 09/22] Make the BatchingEngine not jittery - Stop having threads compete for a single BufferBuilder - ...by skirting around minecraft's BufferSource - Begin work on making vertex writing sane --- .../backend/instancing/BatchExecutor.java | 15 +- .../backend/instancing/InstanceWorld.java | 2 +- .../backend/instancing/RenderDispatcher.java | 3 +- .../instancing/batching/BatchedMaterial.java | 4 +- .../batching/BatchedMaterialGroup.java | 39 ++- .../instancing/batching/BatchingEngine.java | 24 +- .../instancing/batching/CPUInstancer.java | 41 +-- .../instancing/batching/FormatContext.java | 8 - .../instancing/InstancedMaterial.java | 4 +- .../instancing/InstancingEngine.java | 2 +- .../flywheel/backend/model/BufferedModel.java | 2 +- .../backend/model/DirectBufferBuilder.java | 14 + .../backend/model/DirectVertexConsumer.java | 15 +- .../flywheel/backend/model/ModelPool.java | 37 ++- .../com/jozufozu/flywheel/core/Formats.java | 2 +- .../core/crumbling/CrumblingRenderer.java | 2 +- .../flywheel/core/model/BlockModel.java | 33 +- .../flywheel/core/model/BlockType.java | 71 ++++ .../jozufozu/flywheel/core/model/Model.java | 22 +- .../flywheel/core/model/ModelPart.java | 132 ++------ .../flywheel/core/model/ModelTransformer.java | 187 +++++------ .../flywheel/core/model/PartBuilder.java | 14 +- .../flywheel/core/model/WorldModel.java | 36 +- .../core/vertex/PosNormalTexReader.java | 97 ++++++ .../core/vertex/PosNormalTexType.java | 38 +++ .../core/vertex/PosTexNormalWriter.java | 36 ++ .../flywheel/core/vertex/VertexType.java | 17 + .../flywheel/mixin/BufferBuilderMixin.java | 7 +- ...lderReader.java => BlockFormatReader.java} | 27 +- .../com/jozufozu/flywheel/util/Color.java | 309 ++++++++++++++++++ .../util/UnsafeBlockFormatReader.java | 103 ++++++ .../flywheel/shaders/data/modelvertex.glsl | 2 +- 32 files changed, 948 insertions(+), 397 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/batching/FormatContext.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/model/BlockType.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java rename src/main/java/com/jozufozu/flywheel/util/{BufferBuilderReader.java => BlockFormatReader.java} (70%) create mode 100644 src/main/java/com/jozufozu/flywheel/util/Color.java create mode 100644 src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java index f3da663c0..da856e57c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java @@ -11,6 +11,7 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.instancing.batching.WaitGroup; import net.minecraft.util.Mth; @@ -104,7 +105,7 @@ public class BatchExecutor implements TaskEngine { Runnable job; // Finish everyone else's work... - while ((job = getNextTask(false)) != null) { + while ((job = this.jobQueue.pollLast()) != null) { processTask(job); } @@ -116,10 +117,10 @@ public class BatchExecutor implements TaskEngine { } @Nullable - private Runnable getNextTask(boolean block) { - Runnable job = this.jobQueue.poll(); + private Runnable getNextTask() { + Runnable job = this.jobQueue.pollFirst(); - if (job == null && block) { + if (job == null) { synchronized (BatchExecutor.this.jobNotifier) { try { BatchExecutor.this.jobNotifier.wait(); @@ -134,6 +135,8 @@ public class BatchExecutor implements TaskEngine { private void processTask(Runnable job) { try { job.run(); + } catch (Exception e) { + Flywheel.log.error(e); } finally { BatchExecutor.this.wg.done(); } @@ -158,13 +161,13 @@ public class BatchExecutor implements TaskEngine { public void run() { // Run until the chunk builder shuts down while (this.running.get()) { - Runnable job = BatchExecutor.this.getNextTask(true); + Runnable job = BatchExecutor.this.getNextTask(); if (job == null) { continue; } - processTask(job); + BatchExecutor.this.processTask(job); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 10862c154..1b85c8ed6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -116,7 +116,7 @@ public class InstanceWorld { */ public void renderLayer(RenderLayerEvent event) { taskEngine.syncPoint(); - engine.render(event, event.buffers.bufferSource()); + engine.render(event); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java index c54d60205..ae9ccf88f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java @@ -10,9 +10,8 @@ public interface RenderDispatcher { * Render every model for every material. * * @param event Context for rendering. - * @param buffers The buffer source for which batched rendering should happen. */ - void render(RenderLayerEvent event, MultiBufferSource buffers); + void render(RenderLayerEvent event); /** * Maintain the integer origin coordinate to be within a certain distance from the camera in all directions. diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java index 1b314a10a..cf97a5b1b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterial.java @@ -28,9 +28,9 @@ public class BatchedMaterial implements Material { return models.computeIfAbsent(key, $ -> new CPUInstancer<>(type, modelSupplier.get())); } - public void render(PoseStack stack, VertexConsumer buffer, FormatContext context) { + public void setupAndRenderInto(PoseStack stack, VertexConsumer buffer) { for (CPUInstancer instancer : models.values()) { - instancer.setup(context); + instancer.setup(); instancer.drawAll(stack, buffer); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java index 1dd50c414..ba5d71105 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java @@ -42,41 +42,44 @@ public class BatchedMaterialGroup implements MaterialGroup { if (buffer instanceof DirectBufferBuilder direct) { renderParallel(stack, pool, direct); } else { - renderSerial(stack, buffer, FormatContext.defaultContext()); + renderSerial(stack, buffer); } } private void renderParallel(PoseStack stack, TaskEngine pool, DirectBufferBuilder direct) { - int vertexCount = calculateNeededVertices(); + int vertexCount = 0; + for (BatchedMaterial material : materials.values()) { + for (CPUInstancer instancer : material.models.values()) { + instancer.setup(); + vertexCount += instancer.getTotalVertexCount(); + } + } + DirectVertexConsumer consumer = direct.intoDirectConsumer(vertexCount); - FormatContext context = new FormatContext(consumer.hasOverlay()); + + // avoids rendering garbage, but doesn't fix the issue of some instances not being buffered + consumer.memSetZero(); for (BatchedMaterial material : materials.values()) { for (CPUInstancer instancer : material.models.values()) { - instancer.setup(context); - + if (consumer.hasOverlay()) { + instancer.sbb.context.fullNormalTransform = false; + instancer.sbb.context.outputColorDiffuse = false; + } else { + instancer.sbb.context.fullNormalTransform = false; + instancer.sbb.context.outputColorDiffuse = true; + } instancer.submitTasks(stack, pool, consumer); } } } - private void renderSerial(PoseStack stack, VertexConsumer consumer, FormatContext context) { + private void renderSerial(PoseStack stack, VertexConsumer consumer) { for (BatchedMaterial value : materials.values()) { - value.render(stack, consumer, context); + value.setupAndRenderInto(stack, consumer); } } - private int calculateNeededVertices() { - int total = 0; - for (BatchedMaterial material : materials.values()) { - for (CPUInstancer instancer : material.models.values()) { - total += instancer.getTotalVertexCount(); - } - } - - return total; - } - public void clear() { materials.values().forEach(BatchedMaterial::clear); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index 1d49062c9..7af1eb14a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -9,7 +9,9 @@ import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.event.RenderLayerEvent; +import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Camera; import net.minecraft.client.renderer.MultiBufferSource; @@ -17,8 +19,9 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; -public class BatchingEngine implements Engine { +public class BatchingEngine implements Engine, MultiBufferSource { + protected final Map buffers = new HashMap<>(); protected final Map> layers; protected final TaskEngine taskEngine; @@ -42,7 +45,7 @@ public class BatchingEngine implements Engine { } @Override - public void render(RenderLayerEvent event, MultiBufferSource buffers) { + public void render(RenderLayerEvent event) { PoseStack stack = event.stack; stack.pushPose(); @@ -50,14 +53,27 @@ public class BatchingEngine implements Engine { stack.translate(-event.camX, -event.camY, -event.camZ); for (BatchedMaterialGroup group : layers.get(event.getLayer()).values()) { - group.render(stack, buffers, taskEngine); + group.render(stack, this, taskEngine); } taskEngine.syncPoint(); stack.popPose(); - event.buffers.bufferSource().endBatch(); + // TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders + // into the RenderBuffers from context. + buffers.forEach((type, builder) -> type.end(builder, 0, 0, 0)); + } + + @Override + public VertexConsumer getBuffer(RenderType renderType) { + BufferBuilder builder = buffers.computeIfAbsent(renderType, type -> new BufferBuilder(type.bufferSize())); + + if (!builder.building()) { + builder.begin(renderType.mode(), renderType.format()); + } + + return builder; } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 1c39a076d..1486ef14e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -2,12 +2,12 @@ package com.jozufozu.flywheel.backend.instancing.batching; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.Batched; -import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.ModelTransformer; +import com.jozufozu.flywheel.util.Color; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -15,15 +15,14 @@ public class CPUInstancer extends AbstractInstancer { private final Batched batchingType; - private final ModelTransformer sbb; - private final ModelTransformer.Params defaultParams; + final ModelTransformer sbb; public CPUInstancer(Batched type, Model modelData) { super(type::create, modelData); batchingType = type; sbb = new ModelTransformer(modelData); - defaultParams = ModelTransformer.Params.defaultParams(); + modelData.configure(sbb.context); } void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) { @@ -42,43 +41,47 @@ public class CPUInstancer extends AbstractInstancer { } } - @Override - public void notifyDirty() { - // noop - } - private void drawRange(PoseStack stack, VertexConsumer buffer, int from, int to) { - ModelTransformer.Params params = defaultParams.copy(); + ModelTransformer.Params params = new ModelTransformer.Params(); + + // Color color = Color.generateFromLong(from); for (D d : data.subList(from, to)) { + params.loadDefault(); + batchingType.transform(d, params); - sbb.renderInto(params, stack, buffer); + //params.color(color.getRGB()); - params.load(defaultParams); + sbb.renderInto(params, stack, buffer); } } void drawAll(PoseStack stack, VertexConsumer buffer) { - ModelTransformer.Params params = defaultParams.copy(); + ModelTransformer.Params params = new ModelTransformer.Params(); for (D d : data) { + params.loadDefault(); + batchingType.transform(d, params); sbb.renderInto(params, stack, buffer); - - params.load(defaultParams); } } - void setup(FormatContext context) { + void setup() { if (anyToRemove) { - removeDeletedInstances(); + data.removeIf(InstanceData::isRemoved); anyToRemove = false; } - if (context.usesOverlay()) { - defaultParams.overlay(); + if (false) { + this.sbb.context.outputColorDiffuse = false; + this.sbb.context.fullNormalTransform = false; } } + @Override + public void notifyDirty() { + // noop + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/FormatContext.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/FormatContext.java deleted file mode 100644 index bb0bbd41e..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/FormatContext.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.batching; - -public record FormatContext(boolean usesOverlay) { - - public static FormatContext defaultContext() { - return new FormatContext(false); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java index a8229424b..d60a18926 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java @@ -14,8 +14,8 @@ import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.model.ImmediateAllocator; import com.jozufozu.flywheel.backend.model.ModelAllocator; import com.jozufozu.flywheel.backend.model.ModelPool; -import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.model.Model; +import com.jozufozu.flywheel.core.vertex.PosNormalTexType; /** * A collection of Instancers that all have the same format. @@ -33,7 +33,7 @@ public class InstancedMaterial implements Material { if (Backend.getInstance().compat.onAMDWindows()) { allocator = ImmediateAllocator.INSTANCE; } else { - allocator = new ModelPool(Formats.UNLIT_MODEL, 64); + allocator = new ModelPool(PosNormalTexType.INSTANCE, 64); } this.models = CacheBuilder.newBuilder() .removalListener(notification -> { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index af045697a..63015b349 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -82,7 +82,7 @@ public class InstancingEngine

implements Engine { * Render every model for every material. */ @Override - public void render(RenderLayerEvent event, MultiBufferSource buffers) { + public void render(RenderLayerEvent event) { double camX; double camY; double camZ; diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java index 98d2ef6dc..9ec3effcd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java @@ -34,7 +34,7 @@ public class BufferedModel implements IBufferedModel { // mirror it in system memory so we can write to it, and upload our model. try (MappedBuffer buffer = vbo.getBuffer(0, model.size())) { - model.buffer(new VecBufferWriter(buffer)); + model.getType().copyInto(buffer.unwrap(), model.getReader()); } catch (Exception e) { Flywheel.log.error(String.format("Error uploading model '%s':", model.name()), e); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java index 98449155a..c157aed9b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java @@ -1,6 +1,20 @@ package com.jozufozu.flywheel.backend.model; +import com.mojang.blaze3d.vertex.BufferBuilder; + +/** + * Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory. + */ public interface DirectBufferBuilder { + /** + * Create a DirectVertexConsumer from this BufferBuilder. + * + *

+ * After this function returns, the internal state of the BufferBuilder will be as if + * {@link BufferBuilder#endVertex()} was called vertexCount times. It is up to the callee + * to actually populate the BufferBuilder with vertices using the returned value. + *

+ */ DirectVertexConsumer intoDirectConsumer(int vertexCount); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java index 0286083f6..781ad8693 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java @@ -10,6 +10,11 @@ import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormatElement; +/** + * An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer. + * + * @see DirectBufferBuilder + */ public class DirectVertexConsumer implements VertexConsumer { public final VertexFormat format; private final int stride; @@ -68,12 +73,16 @@ public class DirectVertexConsumer implements VertexConsumer { this.end = parent.vertexBase + (long) maxVertices * this.stride; } + public void memSetZero() { + MemoryUtil.memSet(vertexBase, 0, end - vertexBase); + } + public boolean hasOverlay() { return uv1 >= 0; } /** - * Split off the head of this consumer into a new object and advance our write-pointer. + * Split off the head of this consumer into a new object and advance this object's write-pointer. * @param vertexCount The number of vertices that must be written to the head. * @return The head of this consumer. */ @@ -102,8 +111,8 @@ public class DirectVertexConsumer implements VertexConsumer { public VertexConsumer color(int r, int g, int b, int a) { if (color < 0) return this; long base = vertexBase + color; - int color = ((r & 0xFF)) | ((g & 0xFF) << 8) | ((b & 0xFF) << 16) | ((a & 0xFF) << 24); - //MemoryUtil.memPutInt(base, color); +// int color = ((r & 0xFF)) | ((g & 0xFF) << 8) | ((b & 0xFF) << 16) | ((a & 0xFF) << 24); +// MemoryUtil.memPutInt(base, color); MemoryUtil.memPutByte(base, (byte) r); MemoryUtil.memPutByte(base + 1, (byte) g); MemoryUtil.memPutByte(base + 2, (byte) b); diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java index 72f7973e2..9d26f2abb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.backend.model; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -13,12 +14,12 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.core.model.VecBufferWriter; +import com.jozufozu.flywheel.core.vertex.VertexType; import com.jozufozu.flywheel.util.AttribUtil; public class ModelPool implements ModelAllocator { - protected final VertexFormat format; + protected final VertexType vertexType; private final List models = new ArrayList<>(); @@ -32,9 +33,9 @@ public class ModelPool implements ModelAllocator { private boolean dirty; private boolean anyToRemove; - public ModelPool(VertexFormat format, int initialSize) { - this.format = format; - bufferSize = format.getStride() * initialSize; + public ModelPool(VertexType vertexType, int initialSize) { + this.vertexType = vertexType; + bufferSize = vertexType.getStride() * initialSize; vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); @@ -104,7 +105,7 @@ public class ModelPool implements ModelAllocator { * @return true if the buffer was reallocated */ private boolean realloc() { - int neededSize = vertices * format.getStride(); + int neededSize = vertices * vertexType.getStride(); if (neededSize > bufferSize) { bufferSize = neededSize + 128; vbo.alloc(bufferSize); @@ -117,14 +118,14 @@ public class ModelPool implements ModelAllocator { private void uploadAll() { try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { - - VecBufferWriter consumer = new VecBufferWriter(buffer); + ByteBuffer buf = buffer.unwrap(); int vertices = 0; for (PooledModel model : models) { model.first = vertices; - model.model.buffer(consumer); - if (model.callback != null) model.callback.onAlloc(model); + + buffer(buf, model); + vertices += model.getVertexCount(); } @@ -135,14 +136,9 @@ public class ModelPool implements ModelAllocator { private void uploadPending() { try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { - VecBufferWriter consumer = new VecBufferWriter(buffer); - - int stride = format.getStride(); + ByteBuffer buf = buffer.unwrap(); for (PooledModel model : pendingUpload) { - int pos = model.first * stride; - buffer.position(pos); - model.model.buffer(consumer); - if (model.callback != null) model.callback.onAlloc(model); + buffer(buf, model); } pendingUpload.clear(); } catch (Exception e) { @@ -150,6 +146,13 @@ public class ModelPool implements ModelAllocator { } } + private void buffer(ByteBuffer buf, PooledModel model) { + int pos = model.first * vertexType.getStride(); + buf.position(pos); + vertexType.copyInto(buf, model.model.getReader()); + if (model.callback != null) model.callback.onAlloc(model); + } + private void setDirty() { dirty = true; } diff --git a/src/main/java/com/jozufozu/flywheel/core/Formats.java b/src/main/java/com/jozufozu/flywheel/core/Formats.java index 6632c2117..fcd25a452 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Formats.java +++ b/src/main/java/com/jozufozu/flywheel/core/Formats.java @@ -6,7 +6,7 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; public class Formats { public static final VertexFormat UNLIT_MODEL = VertexFormat.builder() - .addAttributes(CommonAttributes.VEC3, CommonAttributes.NORMAL, CommonAttributes.UV) + .addAttributes(CommonAttributes.VEC3, CommonAttributes.UV, CommonAttributes.NORMAL) .build(); public static final VertexFormat COLORED_LIT_MODEL = VertexFormat.builder() diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index f15d3ea91..57f75d84f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -77,7 +77,7 @@ public class CrumblingRenderer { instanceManager.beginFrame(info); - materials.render(event, null); + materials.render(event); instanceManager.invalidate(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java index 172cf217a..c33013ed3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java @@ -1,12 +1,8 @@ package com.jozufozu.flywheel.core.model; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.core.Formats; -import com.jozufozu.flywheel.util.BufferBuilderReader; +import com.jozufozu.flywheel.util.UnsafeBlockFormatReader; import com.jozufozu.flywheel.util.ModelReader; -import com.jozufozu.flywheel.util.RenderMath; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.model.BakedModel; @@ -33,38 +29,27 @@ public class BlockModel implements Model { } public BlockModel(BakedModel model, BlockState referenceState, PoseStack ms) { - reader = new BufferBuilderReader(ModelUtil.getBufferBuilder(model, referenceState, ms)); + reader = new UnsafeBlockFormatReader(ModelUtil.getBufferBuilder(model, referenceState, ms)); name = referenceState.toString(); } + @Override + public void configure(ModelTransformer.Context ctx) { + ctx.inputHasDiffuse = true; + } + @Override public String name() { return name; } - @Override - public VertexFormat format() { - return Formats.UNLIT_MODEL; - } - @Override public int vertexCount() { return reader.getVertexCount(); } @Override - public void buffer(VertexConsumer buffer) { - - int vertexCount = vertexCount(); - - for (int i = 0; i < vertexCount; i++) { - buffer.vertex(reader.getX(i), reader.getY(i), reader.getZ(i)); - - buffer.normal(reader.getNX(i), reader.getNY(i), reader.getNZ(i)); - - buffer.uv(reader.getU(i), reader.getV(i)); - - buffer.endVertex(); - } + public ModelReader getReader() { + return reader; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockType.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockType.java new file mode 100644 index 000000000..c766c89d3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockType.java @@ -0,0 +1,71 @@ +package com.jozufozu.flywheel.core.model; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.core.vertex.VertexType; +import com.jozufozu.flywheel.util.ModelReader; +import com.jozufozu.flywheel.util.RenderMath; + +import net.minecraft.client.renderer.LightTexture; + +public class BlockType implements VertexType { + + public static final BlockType INSTANCE = new BlockType(); + + @Override + public VertexFormat getFormat() { + return Formats.COLORED_LIT_MODEL; + } + + @Override + public void copyInto(ByteBuffer buffer, ModelReader reader) { + int stride = getStride(); + long addr = MemoryUtil.memAddress(buffer); + + int vertexCount = reader.getVertexCount(); + for (int i = 0; i < vertexCount; i++) { + float x = reader.getX(i); + float y = reader.getY(i); + float z = reader.getZ(i); + + float xN = reader.getNX(i); + float yN = reader.getNY(i); + float zN = reader.getNZ(i); + + float u = reader.getU(i); + float v = reader.getV(i); + + byte r = reader.getR(i); + byte g = reader.getG(i); + byte b = reader.getB(i); + byte a = reader.getA(i); + + int light = reader.getLight(i); + + MemoryUtil.memPutFloat(addr, x); + MemoryUtil.memPutFloat(addr + 4, y); + MemoryUtil.memPutFloat(addr + 8, z); + MemoryUtil.memPutByte(addr + 12, RenderMath.nb(xN)); + MemoryUtil.memPutByte(addr + 13, RenderMath.nb(yN)); + MemoryUtil.memPutByte(addr + 14, RenderMath.nb(zN)); + MemoryUtil.memPutFloat(addr + 15, u); + MemoryUtil.memPutFloat(addr + 19, v); + MemoryUtil.memPutByte(addr + 23, r); + MemoryUtil.memPutByte(addr + 24, g); + MemoryUtil.memPutByte(addr + 25, b); + MemoryUtil.memPutByte(addr + 26, a); + + byte block = (byte) (LightTexture.block(light) << 4); + byte sky = (byte) (LightTexture.sky(light) << 4); + + MemoryUtil.memPutByte(addr + 27, block); + MemoryUtil.memPutByte(addr + 28, sky); + + addr += stride; + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Model.java b/src/main/java/com/jozufozu/flywheel/core/model/Model.java index 863cef816..631cb2362 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/Model.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/Model.java @@ -3,8 +3,9 @@ package com.jozufozu.flywheel.core.model; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.model.ElementBuffer; import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.core.vertex.PosNormalTexType; +import com.jozufozu.flywheel.core.vertex.VertexType; import com.jozufozu.flywheel.util.ModelReader; -import com.mojang.blaze3d.vertex.VertexConsumer; /** * A model that can be rendered by flywheel. @@ -33,20 +34,27 @@ public interface Model { */ String name(); - /** - * Copy this model into the given buffer. - */ - void buffer(VertexConsumer buffer); + ModelReader getReader(); /** * @return The number of vertices the model has. */ int vertexCount(); + default void configure(ModelTransformer.Context ctx) { + + } + + default VertexType getType() { + return PosNormalTexType.INSTANCE; + } + /** * @return The format of this model's vertices */ - VertexFormat format(); + default VertexFormat format() { + return getType().getFormat(); + } /** * Create an element buffer object that indexes the vertices of this model. @@ -77,6 +85,4 @@ public interface Model { default boolean empty() { return vertexCount() == 0; } - - ModelReader getReader(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java index 0b87087fb..854779e69 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java @@ -3,29 +3,35 @@ package com.jozufozu.flywheel.core.model; import java.nio.ByteBuffer; import java.util.List; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.core.vertex.PosNormalTexReader; +import com.jozufozu.flywheel.core.vertex.PosTexNormalWriter; import com.jozufozu.flywheel.util.ModelReader; -import com.jozufozu.flywheel.util.RenderMath; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.math.Vector3f; +import com.mojang.blaze3d.platform.MemoryTracker; public class ModelPart implements Model { - private final List cuboids; - private int vertices; + private final int vertices; private final String name; + private final PosNormalTexReader reader; public ModelPart(List cuboids, String name) { - this.cuboids = cuboids; this.name = name; - vertices = 0; - - for (PartBuilder.CuboidBuilder cuboid : cuboids) { - vertices += cuboid.vertices(); + { + int vertices = 0; + for (PartBuilder.CuboidBuilder cuboid : cuboids) { + vertices += cuboid.vertices(); + } + this.vertices = vertices; } + + ByteBuffer buffer = MemoryTracker.create(size()); + PosTexNormalWriter writer = new PosTexNormalWriter(buffer); + for (PartBuilder.CuboidBuilder cuboid : cuboids) { + cuboid.buffer(writer); + } + + reader = new PosNormalTexReader(buffer, vertices); } public static PartBuilder builder(String name, int sizeU, int sizeV) { @@ -37,111 +43,13 @@ public class ModelPart implements Model { return name; } - @Override - public void buffer(VertexConsumer buffer) { - for (PartBuilder.CuboidBuilder cuboid : cuboids) { - cuboid.buffer(buffer); - } - } - @Override public int vertexCount() { return vertices; } - @Override - public VertexFormat format() { - return Formats.UNLIT_MODEL; - } - @Override public ModelReader getReader() { - return new PartReader(this); - } - - private class PartReader implements ModelReader { - - private final ByteBuffer buffer; - - private PartReader(ModelPart part) { - this.buffer = ByteBuffer.allocate(part.size()); - VecBufferWriter writer = new VecBufferWriter(this.buffer); - - buffer(writer); - } - - private int vertIdx(int vertexIndex) { - return vertexIndex * format().getStride(); - } - - @Override - public float getX(int index) { - return buffer.getFloat(vertIdx(index)); - } - - @Override - public float getY(int index) { - return buffer.getFloat(vertIdx(index) + 4); - } - - @Override - public float getZ(int index) { - return buffer.getFloat(vertIdx(index) + 8); - } - - @Override - public byte getR(int index) { - return (byte) 0xFF; - } - - @Override - public byte getG(int index) { - return (byte) 0xFF; - } - - @Override - public byte getB(int index) { - return (byte) 0xFF; - } - - @Override - public byte getA(int index) { - return (byte) 0xFF; - } - - @Override - public float getU(int index) { - return buffer.getFloat(vertIdx(index) + 15); - } - - @Override - public float getV(int index) { - return buffer.getFloat(vertIdx(index) + 19); - } - - @Override - public int getLight(int index) { - return 0; - } - - @Override - public float getNX(int index) { - return RenderMath.f(buffer.get(vertIdx(index) + 12)); - } - - @Override - public float getNY(int index) { - return RenderMath.f(buffer.get(vertIdx(index) + 13)); - } - - @Override - public float getNZ(int index) { - return RenderMath.f(buffer.get(vertIdx(index) + 14)); - } - - @Override - public int getVertexCount() { - return vertices; - } + return reader; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java index 4a8b6bf72..d5e1dbcab 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.core.model; import com.jozufozu.flywheel.util.ModelReader; +import com.jozufozu.flywheel.util.RenderMath; import com.jozufozu.flywheel.util.transform.Transform; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -16,6 +17,8 @@ public class ModelTransformer { private final Model model; private final ModelReader reader; + public final Context context = new Context(); + public ModelTransformer(Model model) { this.model = model; reader = model.getReader(); @@ -34,7 +37,7 @@ public class ModelTransformer { modelMat.multiply(params.model); Matrix3f normalMat; - if (params.fullNormalTransform) { + if (context.fullNormalTransform) { normalMat = input.last().normal().copy(); normalMat.mul(params.normal); } else { @@ -61,42 +64,46 @@ public class ModelTransformer { float ny = normal.y(); float nz = normal.z(); - float instanceDiffuse = LightUtil.diffuseLight(nx, ny, nz); - - switch (params.colorMode) { - case MODEL_ONLY -> builder.color(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i)); - case DIFFUSE_ONLY -> builder.color(instanceDiffuse, instanceDiffuse, instanceDiffuse, 1f); - case MODEL_DIFFUSE -> { - int r = Byte.toUnsignedInt(reader.getR(i)); - int g = Byte.toUnsignedInt(reader.getG(i)); - int b = Byte.toUnsignedInt(reader.getB(i)); - int a = Byte.toUnsignedInt(reader.getA(i)); - - float diffuse = switch (params.diffuseMode) { - case NONE -> 1f; - case INSTANCE -> instanceDiffuse; - case ONE_OVER_STATIC -> 1 / LightUtil.diffuseLight(normalX, normalY, normalZ); - case INSTANCE_OVER_STATIC -> instanceDiffuse / LightUtil.diffuseLight(normalX, normalY, normalZ); - }; - - if (diffuse != 1) { - r = transformColor(r, diffuse); - g = transformColor(g, diffuse); - b = transformColor(b, diffuse); - } - - builder.color(r, g, b, a); - } - case RECOLOR -> { - if (params.diffuseMode == DiffuseMode.NONE) { - builder.color(params.r, params.g, params.b, params.a); - } else { + if (params.useParamColor) { + if (context.outputColorDiffuse) { + float instanceDiffuse = LightUtil.diffuseLight(nx, ny, nz); int colorR = transformColor(params.r, instanceDiffuse); int colorG = transformColor(params.g, instanceDiffuse); int colorB = transformColor(params.b, instanceDiffuse); builder.color(colorR, colorG, colorB, params.a); + } else { + builder.color(params.r, params.g, params.b, params.a); + } + } else { + if (context.inputHasDiffuse) { + int r = Byte.toUnsignedInt(reader.getR(i)); + int g = Byte.toUnsignedInt(reader.getG(i)); + int b = Byte.toUnsignedInt(reader.getB(i)); + int a = Byte.toUnsignedInt(reader.getA(i)); + + float undoStaticDiffuse = 1 / LightUtil.diffuseLight(normalX, normalY, normalZ); + float diffuse; + if (context.outputColorDiffuse) { + diffuse = LightUtil.diffuseLight(nx, ny, nz) * undoStaticDiffuse; + } else { + diffuse = undoStaticDiffuse; + } + + if (diffuse != 1) { + r = transformColor(r, diffuse); + g = transformColor(g, diffuse); + b = transformColor(b, diffuse); + } + + builder.color(r, g, b, a); + } else { + if (context.outputColorDiffuse) { + int d = RenderMath.unb(LightUtil.diffuseLight(nx, ny, nz)); + builder.color(d, d, d, 0xFF); + } else { + builder.color(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i)); + } } - } } //builder.color(Math.max(0, (int) (nx * 255)), Math.max(0, (int) (ny * 255)), Math.max(0, (int) (nz * 255)), 0xFF); @@ -110,11 +117,10 @@ public class ModelTransformer { builder.uv(u, v); } - if (params.hasOverlay) { - builder.overlayCoords(params.overlay); - } + // not always used, but will be ignored by formats that don't use it + builder.overlayCoords(params.overlay); - builder.uv2(params.hasCustomLight ? params.packedLightCoords : reader.getLight(i)); + builder.uv2(params.useParamLight ? params.packedLightCoords : reader.getLight(i)); builder.normal(nx, ny, nz); @@ -128,7 +134,7 @@ public class ModelTransformer { @Override public String toString() { - return "SuperByteBuffer[" + model + ']'; + return "ModelTransformer[" + model + ']'; } public static int transformColor(int component, float scale) { @@ -140,28 +146,32 @@ public class ModelTransformer { void shift(VertexConsumer builder, float u, float v); } - public enum ColorMode { - MODEL_ONLY, - MODEL_DIFFUSE, - DIFFUSE_ONLY, - RECOLOR - } + public static class Context { + /** + * Do we need to include the PoseStack transforms in our transformation of the normal? + */ + public boolean fullNormalTransform = false; + + /** + * Does the model we're transforming have diffuse light baked into its colors? + */ + public boolean inputHasDiffuse = false; + + /** + * Do we need to bake diffuse lighting into the output colors? + */ + public boolean outputColorDiffuse = true; - public enum DiffuseMode { - NONE, - INSTANCE, - ONE_OVER_STATIC, - INSTANCE_OVER_STATIC, } public static class Params implements Transform { - // Vertex Position + + // Transform public final Matrix4f model; public final Matrix3f normal; // Vertex Coloring - public ColorMode colorMode; - public DiffuseMode diffuseMode; + public boolean useParamColor; public int r; public int g; public int b; @@ -171,46 +181,47 @@ public class ModelTransformer { public SpriteShiftFunc spriteShiftFunc; // Vertex Overlay Color - public boolean hasOverlay; public int overlay; // Vertex Lighting - public boolean hasCustomLight; + public boolean useParamLight; public int packedLightCoords; - // Vertex Normals - public boolean fullNormalTransform; - public Params() { model = new Matrix4f(); normal = new Matrix3f(); } + public void loadDefault() { + model.setIdentity(); + normal.setIdentity(); + useParamColor = true; + r = 0xFF; + g = 0xFF; + b = 0xFF; + a = 0xFF; + spriteShiftFunc = null; + overlay = OverlayTexture.NO_OVERLAY; + useParamLight = false; + packedLightCoords = LightTexture.FULL_BRIGHT; + } + public void load(Params from) { model.load(from.model); normal.load(from.normal); - colorMode = from.colorMode; - diffuseMode = from.diffuseMode; + useParamColor = from.useParamColor; r = from.r; g = from.g; b = from.b; a = from.a; spriteShiftFunc = from.spriteShiftFunc; - hasOverlay = from.hasOverlay; overlay = from.overlay; - hasCustomLight = from.hasCustomLight; + useParamLight = from.useParamLight; packedLightCoords = from.packedLightCoords; - fullNormalTransform = from.fullNormalTransform; - } - - public Params copy() { - Params params = new Params(); - params.load(this); - return params; } public Params color(int r, int g, int b, int a) { - this.colorMode = ColorMode.RECOLOR; + this.useParamColor = true; this.r = r; this.g = g; this.b = b; @@ -219,7 +230,7 @@ public class ModelTransformer { } public Params color(byte r, byte g, byte b, byte a) { - this.colorMode = ColorMode.RECOLOR; + this.useParamColor = true; this.r = Byte.toUnsignedInt(r); this.g = Byte.toUnsignedInt(g); this.b = Byte.toUnsignedInt(b); @@ -228,7 +239,7 @@ public class ModelTransformer { } public Params color(int color) { - this.colorMode = ColorMode.RECOLOR; + this.useParamColor = true; this.r = ((color >> 16) & 0xFF); this.g = ((color >> 8) & 0xFF); this.b = (color & 0xFF); @@ -241,29 +252,13 @@ public class ModelTransformer { return this; } - public Params overlay() { - this.hasOverlay = true; - return this; - } - public Params overlay(int overlay) { - this.hasOverlay = true; this.overlay = overlay; return this; } - /** - * Transforms normals not only by the local matrix stack, but also by the passed matrix stack. - */ - public void entityMode() { - this.hasOverlay = true; - this.fullNormalTransform = true; - this.diffuseMode = DiffuseMode.NONE; - this.colorMode = ColorMode.RECOLOR; - } - public Params light(int packedLightCoords) { - this.hasCustomLight = true; + this.useParamLight = true; this.packedLightCoords = packedLightCoords; return this; } @@ -313,23 +308,5 @@ public class ModelTransformer { return this; } - public static Params defaultParams() { - Params out = new Params(); - out.model.setIdentity(); - out.normal.setIdentity(); - out.colorMode = ColorMode.DIFFUSE_ONLY; - out.diffuseMode = DiffuseMode.INSTANCE; - out.r = 0xFF; - out.g = 0xFF; - out.b = 0xFF; - out.a = 0xFF; - out.spriteShiftFunc = null; - out.hasOverlay = false; - out.overlay = OverlayTexture.NO_OVERLAY; - out.hasCustomLight = false; - out.packedLightCoords = LightTexture.FULL_BRIGHT; - out.fullNormalTransform = false; - return out; - } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java b/src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java index 0fb6f2383..f96ba4cab 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java @@ -5,7 +5,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Set; -import com.mojang.blaze3d.vertex.VertexConsumer; +import com.jozufozu.flywheel.core.vertex.PosTexNormalWriter; import com.mojang.math.Matrix3f; import com.mojang.math.Quaternion; import com.mojang.math.Vector3f; @@ -160,7 +160,7 @@ public class PartBuilder { return visibleFaces.size() * 4; } - public void buffer(VertexConsumer buffer) { + public void buffer(PosTexNormalWriter buffer) { float sizeX = posX2 - posX1; float sizeY = posY2 - posY1; @@ -235,11 +235,11 @@ public class PartBuilder { } } - public void quad(VertexConsumer buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) { - buffer.vertex(vertices[0].x(), vertices[0].y(), vertices[0].z()).normal(normal.x(), normal.y(), normal.z()).uv(maxU, minV).endVertex(); - buffer.vertex(vertices[1].x(), vertices[1].y(), vertices[1].z()).normal(normal.x(), normal.y(), normal.z()).uv(minU, minV).endVertex(); - buffer.vertex(vertices[2].x(), vertices[2].y(), vertices[2].z()).normal(normal.x(), normal.y(), normal.z()).uv(minU, maxV).endVertex(); - buffer.vertex(vertices[3].x(), vertices[3].y(), vertices[3].z()).normal(normal.x(), normal.y(), normal.z()).uv(maxU, maxV).endVertex(); + public void quad(PosTexNormalWriter buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) { + buffer.putVertex(vertices[0].x(), vertices[0].y(), vertices[0].z(), normal.x(), normal.y(), normal.z(), maxU, minV); + buffer.putVertex(vertices[1].x(), vertices[1].y(), vertices[1].z(), normal.x(), normal.y(), normal.z(), minU, minV); + buffer.putVertex(vertices[2].x(), vertices[2].y(), vertices[2].z(), normal.x(), normal.y(), normal.z(), minU, maxV); + buffer.putVertex(vertices[3].x(), vertices[3].y(), vertices[3].z(), normal.x(), normal.y(), normal.z(), maxU, maxV); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java index c5f5dff2e..4d61f4c80 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java @@ -2,14 +2,10 @@ package com.jozufozu.flywheel.core.model; import java.util.Collection; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.core.Formats; -import com.jozufozu.flywheel.util.BufferBuilderReader; +import com.jozufozu.flywheel.core.vertex.VertexType; +import com.jozufozu.flywheel.util.UnsafeBlockFormatReader; import com.jozufozu.flywheel.util.ModelReader; -import com.jozufozu.flywheel.util.RenderMath; -import com.mojang.blaze3d.vertex.VertexConsumer; -import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderType; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; @@ -20,7 +16,7 @@ public class WorldModel implements Model { private final String name; public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection blocks, String name) { - reader = new BufferBuilderReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); + reader = new UnsafeBlockFormatReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); this.name = name; } @@ -30,25 +26,8 @@ public class WorldModel implements Model { } @Override - public void buffer(VertexConsumer vertices) { - for (int i = 0; i < vertexCount(); i++) { - vertices.vertex(reader.getX(i), reader.getY(i), reader.getZ(i)); - - vertices.normal(reader.getNX(i), reader.getNY(i), reader.getNZ(i)); - - vertices.uv(reader.getU(i), reader.getV(i)); - - vertices.color(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i)); - - int light = reader.getLight(i); - - byte block = (byte) (LightTexture.block(light) << 4); - byte sky = (byte) (LightTexture.sky(light) << 4); - - vertices.uv2(block, sky); - - vertices.endVertex(); - } + public VertexType getType() { + return BlockType.INSTANCE; } @Override @@ -56,11 +35,6 @@ public class WorldModel implements Model { return reader.getVertexCount(); } - @Override - public VertexFormat format() { - return Formats.COLORED_LIT_MODEL; - } - @Override public ModelReader getReader() { return reader; diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java new file mode 100644 index 000000000..f5c745148 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java @@ -0,0 +1,97 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.util.ModelReader; +import com.jozufozu.flywheel.util.RenderMath; + +import net.minecraft.client.renderer.LightTexture; + +public class PosNormalTexReader implements ModelReader { + + private final ByteBuffer buffer; + private final int vertexCount; + private final long base; + + public PosNormalTexReader(ByteBuffer buffer, int vertexCount) { + this.buffer = buffer; + this.vertexCount = vertexCount; + this.base = MemoryUtil.memAddress(buffer); + } + + private long ptr(long idx) { + return base + idx * 23; + } + + @Override + public float getX(int index) { + return MemoryUtil.memGetFloat(ptr(index)); + } + + @Override + public float getY(int index) { + return MemoryUtil.memGetFloat(ptr(index) + 4); + } + + @Override + public float getZ(int index) { + return MemoryUtil.memGetFloat(ptr(index) + 8); + } + + @Override + public byte getR(int index) { + return (byte) 0xFF; + } + + @Override + public byte getG(int index) { + return (byte) 0xFF; + } + + @Override + public byte getB(int index) { + return (byte) 0xFF; + } + + @Override + public byte getA(int index) { + return (byte) 0xFF; + } + + @Override + public float getU(int index) { + return MemoryUtil.memGetFloat(ptr(index) + 12); + } + + @Override + public float getV(int index) { + return MemoryUtil.memGetFloat(ptr(index) + 16); + } + + @Override + public int getLight(int index) { + return 0; + } + + @Override + public float getNX(int index) { + return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 20)); + } + + @Override + public float getNY(int index) { + return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 21)); + } + + @Override + public float getNZ(int index) { + return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 22)); + } + + @Override + public int getVertexCount() { + return vertexCount; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java new file mode 100644 index 000000000..eb2396848 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java @@ -0,0 +1,38 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.nio.ByteBuffer; + +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.util.ModelReader; + +public class PosNormalTexType implements VertexType { + + public static final PosNormalTexType INSTANCE = new PosNormalTexType(); + + @Override + public VertexFormat getFormat() { + return Formats.UNLIT_MODEL; + } + + @Override + public void copyInto(ByteBuffer buffer, ModelReader reader) { + PosTexNormalWriter writer = new PosTexNormalWriter(buffer); + + int vertexCount = reader.getVertexCount(); + for (int i = 0; i < vertexCount; i++) { + float x = reader.getX(i); + float y = reader.getY(i); + float z = reader.getZ(i); + + float u = reader.getU(i); + float v = reader.getV(i); + + float xN = reader.getNX(i); + float yN = reader.getNY(i); + float zN = reader.getNZ(i); + + writer.putVertex(x, y, z, xN, yN, zN, u, v); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java new file mode 100644 index 000000000..a0a7aa1f7 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java @@ -0,0 +1,36 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.util.RenderMath; + +public class PosTexNormalWriter { + + private long addr; + + private int vertexCount; + + public PosTexNormalWriter(ByteBuffer buffer) { + addr = MemoryUtil.memAddress(buffer); + } + + public void putVertex(float x, float y, float z, float nX, float nY, float nZ, float u, float v) { + MemoryUtil.memPutFloat(addr, x); + MemoryUtil.memPutFloat(addr + 4, y); + MemoryUtil.memPutFloat(addr + 8, z); + MemoryUtil.memPutFloat(addr + 12, u); + MemoryUtil.memPutFloat(addr + 16, v); + MemoryUtil.memPutByte(addr + 20, RenderMath.nb(nX)); + MemoryUtil.memPutByte(addr + 21, RenderMath.nb(nY)); + MemoryUtil.memPutByte(addr + 22, RenderMath.nb(nZ)); + + addr += 23; + vertexCount++; + } + + public int getVertexCount() { + return vertexCount; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java new file mode 100644 index 000000000..24fd80ba1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java @@ -0,0 +1,17 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.nio.ByteBuffer; + +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.util.ModelReader; + +public interface VertexType { + + VertexFormat getFormat(); + + void copyInto(ByteBuffer buffer, ModelReader reader); + + default int getStride() { + return getFormat().getStride(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java index 76dd7f129..66c29bfd4 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.mixin; import java.nio.ByteBuffer; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.spongepowered.asm.mixin.Mixin; @@ -38,9 +39,11 @@ public abstract class BufferBuilderMixin implements DirectBufferBuilder { private int nextElementByte; @Override + @Nonnull public DirectVertexConsumer intoDirectConsumer(int vertexCount) { int bytes = vertexCount * format.getVertexSize(); - ensureCapacity(bytes); + // ensure we have capacity for one extra vertex, BufferBuilder does this on #endVertex + ensureCapacity(bytes + format.getVertexSize()); DirectVertexConsumer consumer = new DirectVertexConsumer(this.buffer, this.format, vertexCount); @@ -49,7 +52,7 @@ public abstract class BufferBuilderMixin implements DirectBufferBuilder { .get(0); this.elementIndex = 0; this.nextElementByte += bytes; - this.buffer.position(consumer.startPos + bytes); + this.buffer.position(this.buffer.position() + bytes); return consumer; } diff --git a/src/main/java/com/jozufozu/flywheel/util/BufferBuilderReader.java b/src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java similarity index 70% rename from src/main/java/com/jozufozu/flywheel/util/BufferBuilderReader.java rename to src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java index eecf24e1b..2372d0036 100644 --- a/src/main/java/com/jozufozu/flywheel/util/BufferBuilderReader.java +++ b/src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java @@ -3,36 +3,24 @@ package com.jozufozu.flywheel.util; import java.nio.ByteBuffer; import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.datafixers.util.Pair; -public class BufferBuilderReader implements ModelReader { +public class BlockFormatReader implements ModelReader { private final ByteBuffer buffer; private final int vertexCount; - private final int formatSize; - private final int size; + private final int stride; - public BufferBuilderReader(BufferBuilder builder) { - VertexFormat vertexFormat = builder.getVertexFormat(); + public BlockFormatReader(BufferBuilder builder) { Pair data = builder.popNextBuffer(); buffer = data.getSecond(); - formatSize = vertexFormat.getVertexSize(); + stride = builder.getVertexFormat() + .getVertexSize(); vertexCount = data.getFirst() .vertexCount(); - size = vertexCount * formatSize; - - // TODO: adjust the getters based on the input format - // ImmutableList elements = vertexFormat.getElements(); - // for (int i = 0, size = elements.size(); i < size; i++) { - // VertexFormatElement element = elements.get(i); - // int offset = vertexFormat.getOffset(i); - // - // element.getUsage() - // } } @Override @@ -41,7 +29,7 @@ public class BufferBuilderReader implements ModelReader { } private int vertIdx(int vertexIndex) { - return vertexIndex * formatSize; + return vertexIndex * stride; } @Override @@ -114,7 +102,4 @@ public class BufferBuilderReader implements ModelReader { return vertexCount; } - public int getSize() { - return size; - } } diff --git a/src/main/java/com/jozufozu/flywheel/util/Color.java b/src/main/java/com/jozufozu/flywheel/util/Color.java new file mode 100644 index 000000000..0a21b84bd --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/Color.java @@ -0,0 +1,309 @@ +package com.jozufozu.flywheel.util; + +import java.util.function.UnaryOperator; + +import javax.annotation.Nonnull; + +import com.google.common.hash.Hashing; +import com.mojang.math.Vector3f; + +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; + +@SuppressWarnings("PointlessBitwiseExpression") +public class Color { + public final static Color TRANSPARENT_BLACK = new Color(0, 0, 0, 0).setImmutable(); + public final static Color BLACK = new Color(0, 0, 0).setImmutable(); + public final static Color WHITE = new Color(255, 255, 255).setImmutable(); + public final static Color RED = new Color(255, 0, 0).setImmutable(); + public final static Color GREEN = new Color(0, 255, 0).setImmutable(); + public final static Color SPRING_GREEN = new Color(0, 255, 187).setImmutable(); + + protected boolean mutable = true; + protected int value; + + public Color(int r, int g, int b) { + this(r, g, b, 0xff); + } + + public Color(int r, int g, int b, int a) { + value = ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff) << 0); + } + + public Color(float r, float g, float b, float a) { + this( + (int) (0.5 + 0xff * Mth.clamp(r, 0, 1)), + (int) (0.5 + 0xff * Mth.clamp(g, 0, 1)), + (int) (0.5 + 0xff * Mth.clamp(b, 0, 1)), + (int) (0.5 + 0xff * Mth.clamp(a, 0, 1)) + ); + } + + public Color(int rgba) { + value = rgba; + } + + public Color(int rgb, boolean hasAlpha) { + if (hasAlpha) { + value = rgb; + } else { + value = rgb | 0xff_000000; + } + } + + public Color copy() { + return copy(true); + } + + public Color copy(boolean mutable) { + if (mutable) + return new Color(value); + else + return new Color(value).setImmutable(); + } + + /** + * Mark this color as immutable. Attempting to mutate this color in the future + * will instead cause a copy to be created that can me modified. + */ + public Color setImmutable() { + this.mutable = false; + return this; + } + + /** + * @return the red component in the range 0-255. + * @see #getRGB + */ + public int getRed() { + return (getRGB() >> 16) & 0xff; + } + + /** + * @return the green component in the range 0-255. + * @see #getRGB + */ + public int getGreen() { + return (getRGB() >> 8) & 0xff; + } + + /** + * @return the blue component in the range 0-255. + * @see #getRGB + */ + public int getBlue() { + return (getRGB() >> 0) & 0xff; + } + + /** + * @return the alpha component in the range 0-255. + * @see #getRGB + */ + public int getAlpha() { + return (getRGB() >> 24) & 0xff; + } + + /** + * @return the red component in the range 0-1f. + */ + public float getRedAsFloat() { + return getRed() / 255f; + } + + /** + * @return the green component in the range 0-1f. + */ + public float getGreenAsFloat() { + return getGreen() / 255f; + } + + /** + * @return the blue component in the range 0-1f. + */ + public float getBlueAsFloat() { + return getBlue() / 255f; + } + + /** + * @return the alpha component in the range 0-1f. + */ + public float getAlphaAsFloat() { + return getAlpha() / 255f; + } + + /** + * Returns the RGB value representing this color + * (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue). + * @return the RGB value of the color + */ + public int getRGB() { + return value; + } + + public Vec3 asVector() { + return new Vec3(getRedAsFloat(), getGreenAsFloat(), getBlueAsFloat()); + } + + public Vector3f asVectorF() { + return new Vector3f(getRedAsFloat(), getGreenAsFloat(), getBlueAsFloat()); + } + + public Color setRed(int r) { + return ensureMutable().setRedUnchecked(r); + } + + public Color setGreen(int g) { + return ensureMutable().setGreenUnchecked(g); + } + + public Color setBlue(int b) { + return ensureMutable().setBlueUnchecked(b); + } + + public Color setAlpha(int a) { + return ensureMutable().setAlphaUnchecked(a); + } + + public Color setRed(float r) { + return ensureMutable().setRedUnchecked((int) (0xff * Mth.clamp(r, 0, 1))); + } + + public Color setGreen(float g) { + return ensureMutable().setGreenUnchecked((int) (0xff * Mth.clamp(g, 0, 1))); + } + + public Color setBlue(float b) { + return ensureMutable().setBlueUnchecked((int) (0xff * Mth.clamp(b, 0, 1))); + } + + public Color setAlpha(float a) { + return ensureMutable().setAlphaUnchecked((int) (0xff * Mth.clamp(a, 0, 1))); + } + + public Color scaleAlpha(float factor) { + return ensureMutable().setAlphaUnchecked((int) (getAlpha() * Mth.clamp(factor, 0, 1))); + } + + public Color mixWith(Color other, float weight) { + return ensureMutable() + .setRedUnchecked((int) (getRed() + (other.getRed() - getRed()) * weight)) + .setGreenUnchecked((int) (getGreen() + (other.getGreen() - getGreen()) * weight)) + .setBlueUnchecked((int) (getBlue() + (other.getBlue() - getBlue()) * weight)) + .setAlphaUnchecked((int) (getAlpha() + (other.getAlpha() - getAlpha()) * weight)); + } + + public Color darker() { + int a = getAlpha(); + return ensureMutable().mixWith(BLACK, .25f).setAlphaUnchecked(a); + } + + public Color brighter() { + int a = getAlpha(); + return ensureMutable().mixWith(WHITE, .25f).setAlphaUnchecked(a); + } + + public Color setValue(int value) { + return ensureMutable().setValueUnchecked(value); + } + + public Color modifyValue(UnaryOperator function) { + int newValue = function.apply(value); + if (newValue == value) + return this; + + return ensureMutable().setValueUnchecked(newValue); + } + + // ********* // + + protected Color ensureMutable() { + if (this.mutable) + return this; + + return new Color(this.value); + } + + protected Color setRedUnchecked(int r) { + this.value = (this.value & 0xff_00ffff) | ((r & 0xff) << 16); + return this; + } + + protected Color setGreenUnchecked(int g) { + this.value = (this.value & 0xff_ff00ff) | ((g & 0xff) << 8); + return this; + } + + protected Color setBlueUnchecked(int b) { + this.value = (this.value & 0xff_ffff00) | ((b & 0xff) << 0); + return this; + } + + protected Color setAlphaUnchecked(int a) { + this.value = (this.value & 0x00_ffffff) | ((a & 0xff) << 24); + return this; + } + + protected Color setValueUnchecked(int value) { + this.value = value; + return this; + } + + // ********* // + + public static Color mixColors(@Nonnull Color c1, @Nonnull Color c2, float w) { + return new Color( + (int) (c1.getRed() + (c2.getRed() - c1.getRed()) * w), + (int) (c1.getGreen() + (c2.getGreen() - c1.getGreen()) * w), + (int) (c1.getBlue() + (c2.getBlue() - c1.getBlue()) * w), + (int) (c1.getAlpha() + (c2.getAlpha() - c1.getAlpha()) * w) + ); + } + + public static int mixColors(int color1, int color2, float w) { + int a1 = (color1 >> 24); + int r1 = (color1 >> 16) & 0xFF; + int g1 = (color1 >> 8) & 0xFF; + int b1 = color1 & 0xFF; + int a2 = (color2 >> 24); + int r2 = (color2 >> 16) & 0xFF; + int g2 = (color2 >> 8) & 0xFF; + int b2 = color2 & 0xFF; + + return + ((int) (a1 + (a2 - a1) * w) << 24) + + ((int) (r1 + (r2 - r1) * w) << 16) + + ((int) (g1 + (g2 - g1) * w) << 8) + + ((int) (b1 + (b2 - b1) * w) << 0); + } + + public static Color rainbowColor(int timeStep) { + int localTimeStep = Math.abs(timeStep) % 1536; + int timeStepInPhase = localTimeStep % 256; + int phaseBlue = localTimeStep / 256; + int red = colorInPhase(phaseBlue + 4, timeStepInPhase); + int green = colorInPhase(phaseBlue + 2, timeStepInPhase); + int blue = colorInPhase(phaseBlue, timeStepInPhase); + return new Color(red, green, blue); + } + + private static int colorInPhase(int phase, int progress) { + phase = phase % 6; + if (phase <= 1) + return 0; + if (phase == 2) + return progress; + if (phase <= 4) + return 255; + else + return 255 - progress; + } + + public static Color generateFromLong(long l) { + return rainbowColor(Hashing.crc32().hashLong(l).asInt()) + .mixWith(WHITE, 0.5f); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java b/src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java new file mode 100644 index 000000000..a81ca4205 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java @@ -0,0 +1,103 @@ +package com.jozufozu.flywheel.util; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.datafixers.util.Pair; + +public class UnsafeBlockFormatReader implements ModelReader { + + private final int vertexCount; + private final int stride; + private final long base; + + public UnsafeBlockFormatReader(BufferBuilder builder) { + VertexFormat vertexFormat = builder.getVertexFormat(); + Pair data = builder.popNextBuffer(); + this.base = MemoryUtil.memAddress(data.getSecond()); + this.vertexCount = data.getFirst().vertexCount(); + this.stride = vertexFormat.getVertexSize(); + } + + private long ptr(long index) { + return base + index * stride; + } + + @Override + public boolean isEmpty() { + return vertexCount == 0; + } + + @Override + public float getX(int index) { + return MemoryUtil.memGetFloat(ptr(index)); + } + + @Override + public float getY(int index) { + return MemoryUtil.memGetFloat(ptr(index) + 4); + } + + @Override + public float getZ(int index) { + return MemoryUtil.memGetFloat(ptr(index) + 8); + } + + @Override + public byte getR(int index) { + return MemoryUtil.memGetByte(ptr(index) + 12); + } + + @Override + public byte getG(int index) { + return MemoryUtil.memGetByte(ptr(index) + 13); + } + + @Override + public byte getB(int index) { + return MemoryUtil.memGetByte(ptr(index) + 14); + } + + @Override + public byte getA(int index) { + return MemoryUtil.memGetByte(ptr(index) + 15); + } + + @Override + public float getU(int index) { + return MemoryUtil.memGetFloat(ptr(index) + 16); + } + + @Override + public float getV(int index) { + return MemoryUtil.memGetFloat(ptr(index) + 20); + } + + @Override + public int getLight(int index) { + return MemoryUtil.memGetInt(ptr(index) + 24); + } + + @Override + public float getNX(int index) { + return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 28)); + } + + @Override + public float getNY(int index) { + return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 29)); + } + + @Override + public float getNZ(int index) { + return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 30)); + } + + @Override + public int getVertexCount() { + return vertexCount; + } +} diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/data/modelvertex.glsl b/src/main/resources/assets/flywheel/flywheel/shaders/data/modelvertex.glsl index ae77b9b68..48e713b43 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/data/modelvertex.glsl +++ b/src/main/resources/assets/flywheel/flywheel/shaders/data/modelvertex.glsl @@ -1,5 +1,5 @@ struct Vertex { vec3 pos; - vec3 normal; vec2 texCoords; + vec3 normal; }; From 42365def02e0170daabf017ecf3985f3952319aa Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 22 Dec 2021 00:22:41 -0800 Subject: [PATCH 10/22] util and core cleanup - Consolidate/audit utility classes - Move more towards sane vertex types --- .../com/jozufozu/flywheel/api/Material.java | 4 +- .../com/jozufozu/flywheel/backend/Loader.java | 4 +- .../backend/instancing/AbstractInstance.java | 2 +- .../instancing/entity/EntityInstance.java | 2 +- .../instancing/InstancedMaterial.java | 4 +- .../instancing/InstancedMaterialGroup.java | 4 +- .../instancing/tile/TileEntityInstance.java | 4 +- .../flywheel/backend/model/BufferedModel.java | 1 - .../backend/source/ShaderSources.java | 4 +- .../com/jozufozu/flywheel/core/Formats.java | 28 +---- .../flywheel/core/atlas/AtlasInfo.java | 48 ------- .../flywheel/core/atlas/SheetData.java | 11 -- .../flywheel/core/crumbling/AtlasInfo.java | 48 +++++++ .../core/crumbling/CrumblingGroup.java | 15 +-- .../core/crumbling/CrumblingProgram.java | 1 - .../core/crumbling/CrumblingRenderer.java | 4 +- .../core/{model => hardcoded}/ModelPart.java | 17 ++- .../{model => hardcoded}/PartBuilder.java | 2 +- .../core/materials/model/ModelType.java | 10 +- .../core/materials/oriented/OrientedData.java | 10 -- .../core/materials/oriented/OrientedType.java | 9 +- .../flywheel/core/model/BlockModel.java | 10 +- .../flywheel/core/model/BlockType.java | 71 ----------- .../jozufozu/flywheel/core/model/Model.java | 8 +- .../flywheel/core/model/ModelTransformer.java | 4 +- .../flywheel/core/model/ModelUtil.java | 13 ++ .../flywheel/core/model/Readable.java | 119 ------------------ .../flywheel/core/model/VecBufferWriter.java | 71 ----------- .../flywheel/core/model/WorldModel.java | 13 +- .../core/shader/GameStateProgram.java | 6 +- .../flywheel/core/vertex/BlockVertex.java | 71 +++++++++++ .../vertex/BlockVertexList.java} | 7 +- .../vertex/BlockVertexListUnsafe.java} | 7 +- ...alTexType.java => PosTexNormalVertex.java} | 13 +- ...java => PosTexNormalVertexListUnsafe.java} | 7 +- .../core/vertex/PosTexNormalWriter.java | 6 + .../vertex/VertexList.java} | 4 +- .../flywheel/core/vertex/VertexType.java | 3 +- .../flywheel/light/GPULightVolume.java | 2 + .../flywheel/light/LightListener.java | 3 + .../jozufozu/flywheel/light/LightUpdater.java | 2 + .../jozufozu/flywheel/light/LightVolume.java | 3 + .../flywheel/mixin/RenderTexturesMixin.java | 4 +- .../flywheel/mixin/atlas/AtlasDataMixin.java | 8 +- .../jozufozu/flywheel/util/AngleHelper.java | 42 ------- .../flywheel/util/AnimationTickHolder.java | 20 +-- .../jozufozu/flywheel/util/AttribUtil.java | 1 + .../java/com/jozufozu/flywheel/util/Pair.java | 37 +----- .../jozufozu/flywheel/util/RenderMath.java | 38 ++++++ .../jozufozu/flywheel/util/RenderUtil.java | 61 --------- .../jozufozu/flywheel/util/StreamUtil.java | 57 --------- .../jozufozu/flywheel/util/StringUtil.java | 58 +++++++++ .../jozufozu/flywheel/util/TextureBinder.java | 14 --- .../{RenderTextures.java => Textures.java} | 18 ++- .../box}/CoordinateConsumer.java | 2 +- .../{light => util/box}/GridAlignedBB.java | 16 +-- .../{light => util/box}/ImmutableBox.java | 4 +- .../com/jozufozu/flywheel/util/vec/Vec3.java | 80 ------------ .../com/jozufozu/flywheel/util/vec/Vec4.java | 66 ---------- .../flywheel/vanilla/BellInstance.java | 2 +- .../flywheel/vanilla/ChestInstance.java | 2 +- .../flywheel/vanilla/MinecartInstance.java | 2 +- .../flywheel/vanilla/ShulkerBoxInstance.java | 2 +- 63 files changed, 391 insertions(+), 818 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/core/atlas/AtlasInfo.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/atlas/SheetData.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java rename src/main/java/com/jozufozu/flywheel/core/{model => hardcoded}/ModelPart.java (66%) rename src/main/java/com/jozufozu/flywheel/core/{model => hardcoded}/PartBuilder.java (99%) delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/BlockType.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/Readable.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java rename src/main/java/com/jozufozu/flywheel/{util/BlockFormatReader.java => core/vertex/BlockVertexList.java} (90%) rename src/main/java/com/jozufozu/flywheel/{util/UnsafeBlockFormatReader.java => core/vertex/BlockVertexListUnsafe.java} (91%) rename src/main/java/com/jozufozu/flywheel/core/vertex/{PosNormalTexType.java => PosTexNormalVertex.java} (63%) rename src/main/java/com/jozufozu/flywheel/core/vertex/{PosNormalTexReader.java => PosTexNormalVertexListUnsafe.java} (88%) rename src/main/java/com/jozufozu/flywheel/{util/ModelReader.java => core/vertex/VertexList.java} (85%) delete mode 100644 src/main/java/com/jozufozu/flywheel/util/AngleHelper.java delete mode 100644 src/main/java/com/jozufozu/flywheel/util/RenderUtil.java delete mode 100644 src/main/java/com/jozufozu/flywheel/util/StreamUtil.java rename src/main/java/com/jozufozu/flywheel/util/{RenderTextures.java => Textures.java} (58%) rename src/main/java/com/jozufozu/flywheel/{light => util/box}/CoordinateConsumer.java (71%) rename src/main/java/com/jozufozu/flywheel/{light => util/box}/GridAlignedBB.java (96%) rename src/main/java/com/jozufozu/flywheel/{light => util/box}/ImmutableBox.java (97%) delete mode 100644 src/main/java/com/jozufozu/flywheel/util/vec/Vec3.java delete mode 100644 src/main/java/com/jozufozu/flywheel/util/vec/Vec4.java diff --git a/src/main/java/com/jozufozu/flywheel/api/Material.java b/src/main/java/com/jozufozu/flywheel/api/Material.java index edcc489d4..252c9fb33 100644 --- a/src/main/java/com/jozufozu/flywheel/api/Material.java +++ b/src/main/java/com/jozufozu/flywheel/api/Material.java @@ -5,8 +5,8 @@ import java.util.function.Supplier; import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.model.BlockModel; import com.jozufozu.flywheel.core.model.Model; +import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.util.Pair; -import com.jozufozu.flywheel.util.RenderUtil; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.core.Direction; @@ -27,7 +27,7 @@ public interface Material { } default Instancer getModel(PartialModel partial, BlockState referenceState, Direction dir) { - return getModel(partial, referenceState, dir, RenderUtil.rotateToFace(dir)); + return getModel(partial, referenceState, dir, ModelUtil.rotateToFace(dir)); } default Instancer getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier modelTransform) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 3d9d6b369..92b675943 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -13,7 +13,7 @@ import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; import com.jozufozu.flywheel.event.GatherContextEvent; import com.jozufozu.flywheel.util.ResourceUtil; -import com.jozufozu.flywheel.util.StreamUtil; +import com.jozufozu.flywheel.util.StringUtil; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; @@ -106,7 +106,7 @@ public class Loader implements ResourceManagerReloadListener { try { Resource file = manager.getResource(location); - String s = StreamUtil.readToString(file.getInputStream()); + String s = StringUtil.readToString(file.getInputStream()); ResourceLocation specName = ResourceUtil.trim(location, PROGRAM_DIR, ".json"); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java index 3bf0c14b2..a194a265c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java @@ -9,7 +9,7 @@ import com.jozufozu.flywheel.api.instance.IInstance; import com.jozufozu.flywheel.api.instance.ITickableInstance; import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; import com.jozufozu.flywheel.core.materials.FlatLit; -import com.jozufozu.flywheel.light.ImmutableBox; +import com.jozufozu.flywheel.util.box.ImmutableBox; import com.jozufozu.flywheel.light.LightListener; import com.jozufozu.flywheel.light.LightProvider; import com.jozufozu.flywheel.light.ListenerStatus; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java index ac6443bf0..e1f6f3830 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java @@ -5,7 +5,7 @@ import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.ITickableInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; -import com.jozufozu.flywheel.light.GridAlignedBB; +import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.light.LightListener; import com.jozufozu.flywheel.light.LightProvider; import com.jozufozu.flywheel.light.MovingListener; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java index d60a18926..a72dcf415 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java @@ -14,8 +14,8 @@ import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.model.ImmediateAllocator; import com.jozufozu.flywheel.backend.model.ModelAllocator; import com.jozufozu.flywheel.backend.model.ModelPool; +import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.core.vertex.PosNormalTexType; /** * A collection of Instancers that all have the same format. @@ -33,7 +33,7 @@ public class InstancedMaterial implements Material { if (Backend.getInstance().compat.onAMDWindows()) { allocator = ImmediateAllocator.INSTANCE; } else { - allocator = new ModelPool(PosNormalTexType.INSTANCE, 64); + allocator = new ModelPool(Formats.POS_TEX_NORMAL, 64); } this.models = CacheBuilder.newBuilder() .removalListener(notification -> { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java index 184aa3be4..f0a432b2d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java @@ -10,7 +10,7 @@ import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.model.ModelPool; import com.jozufozu.flywheel.core.shader.WorldProgram; -import com.jozufozu.flywheel.util.TextureBinder; +import com.jozufozu.flywheel.util.Textures; import com.mojang.math.Matrix4f; import net.minecraft.client.renderer.RenderType; @@ -45,7 +45,7 @@ public class InstancedMaterialGroup

implements MaterialG public void render(Matrix4f viewProjection, double camX, double camY, double camZ) { type.setupRenderState(); - TextureBinder.bindActiveTextures(); + Textures.bindActiveTextures(); renderAll(viewProjection, camX, camY, camZ); type.clearRenderState(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileEntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileEntityInstance.java index b064d214f..59e9b71f1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileEntityInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileEntityInstance.java @@ -8,8 +8,8 @@ import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.materials.model.ModelData; import com.jozufozu.flywheel.core.materials.oriented.OrientedData; -import com.jozufozu.flywheel.light.GridAlignedBB; -import com.jozufozu.flywheel.light.ImmutableBox; +import com.jozufozu.flywheel.util.box.GridAlignedBB; +import com.jozufozu.flywheel.util.box.ImmutableBox; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity; diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java index 9ec3effcd..e73844a0f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java @@ -12,7 +12,6 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.core.model.VecBufferWriter; import com.jozufozu.flywheel.util.AttribUtil; public class BufferedModel implements IBufferedModel { diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java index 88db37083..00898a238 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java @@ -8,7 +8,7 @@ import java.util.Map; import com.google.common.collect.Lists; import com.jozufozu.flywheel.util.ResourceUtil; -import com.jozufozu.flywheel.util.StreamUtil; +import com.jozufozu.flywheel.util.StringUtil; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.Resource; @@ -37,7 +37,7 @@ public class ShaderSources implements ISourceHolder { try { Resource resource = manager.getResource(location); - String source = StreamUtil.readToString(resource.getInputStream()); + String source = StringUtil.readToString(resource.getInputStream()); ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR); diff --git a/src/main/java/com/jozufozu/flywheel/core/Formats.java b/src/main/java/com/jozufozu/flywheel/core/Formats.java index fcd25a452..21f45dd76 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Formats.java +++ b/src/main/java/com/jozufozu/flywheel/core/Formats.java @@ -1,30 +1,10 @@ package com.jozufozu.flywheel.core; -import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; -import com.jozufozu.flywheel.backend.gl.attrib.MatrixAttributes; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.core.vertex.BlockVertex; +import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex; public class Formats { - public static final VertexFormat UNLIT_MODEL = VertexFormat.builder() - .addAttributes(CommonAttributes.VEC3, CommonAttributes.UV, CommonAttributes.NORMAL) - .build(); - public static final VertexFormat COLORED_LIT_MODEL = VertexFormat.builder() - .addAttributes(CommonAttributes.VEC3, - CommonAttributes.NORMAL, - CommonAttributes.UV, - CommonAttributes.RGBA, - CommonAttributes.LIGHT) - .build(); - - public static final VertexFormat TRANSFORMED = litInstance().addAttributes(MatrixAttributes.MAT4, MatrixAttributes.MAT3) - .build(); - - public static final VertexFormat ORIENTED = litInstance().addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION) - .build(); - - public static VertexFormat.Builder litInstance() { - return VertexFormat.builder() - .addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA); - } + public static final PosTexNormalVertex POS_TEX_NORMAL = new PosTexNormalVertex(); + public static final BlockVertex BLOCK = new BlockVertex(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/atlas/AtlasInfo.java b/src/main/java/com/jozufozu/flywheel/core/atlas/AtlasInfo.java deleted file mode 100644 index 6b915c3a9..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/atlas/AtlasInfo.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.jozufozu.flywheel.core.atlas; - -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Nullable; - -import com.jozufozu.flywheel.mixin.atlas.SheetDataAccessor; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.texture.AbstractTexture; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.resources.ResourceLocation; - -public class AtlasInfo { - - private static final Map sheetData = new HashMap<>(); - - public static TextureAtlas getAtlas(ResourceLocation name) { - AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(name); - - if (texture instanceof TextureAtlas) - return (TextureAtlas) texture; - else - return null; - } - - @Nullable - public static SheetData getAtlasData(TextureAtlasSprite texture) { - return getAtlasData(texture.atlas()); - } - - @Nullable - public static SheetData getAtlasData(TextureAtlas atlas) { - return getAtlasData(atlas.location()); - } - - @Nullable - public static SheetData getAtlasData(@Nullable ResourceLocation loc) { - return sheetData.get(loc); - } - - public static void setAtlasData(ResourceLocation atlas, SheetDataAccessor accessor) { - sheetData.put(atlas, new SheetData(accessor.getWidth(), accessor.getHeight())); - } - -} diff --git a/src/main/java/com/jozufozu/flywheel/core/atlas/SheetData.java b/src/main/java/com/jozufozu/flywheel/core/atlas/SheetData.java deleted file mode 100644 index e015ab5cf..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/atlas/SheetData.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.jozufozu.flywheel.core.atlas; - -public class SheetData { - public final int width; - public final int height; - - public SheetData(int width, int height) { - this.width = width; - this.height = height; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java new file mode 100644 index 000000000..a21ba81b8 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java @@ -0,0 +1,48 @@ +package com.jozufozu.flywheel.core.crumbling; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.mixin.atlas.SheetDataAccessor; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.AbstractTexture; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.resources.ResourceLocation; + +/** + * Track width and height of all created texture atlases. + * + * @see com.jozufozu.flywheel.mixin.atlas.AtlasDataMixin + */ +public class AtlasInfo { + + private static final Map sheetData = new HashMap<>(); + + @Nullable + public static TextureAtlas getAtlas(ResourceLocation name) { + AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(name); + + if (texture instanceof TextureAtlas atlas) + return atlas; + else + return null; + } + + @Nullable + public static SheetSize getSheetSize(@Nullable ResourceLocation loc) { + return sheetData.get(loc); + } + + /** + * FOR USE IN MIXIN + */ + public static void _setAtlasData(ResourceLocation atlas, SheetDataAccessor accessor) { + sheetData.put(atlas, new SheetSize(accessor.getWidth(), accessor.getHeight())); + } + + public record SheetSize(int width, int height) { + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java index c0eff27ae..19385b5a3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java @@ -2,10 +2,7 @@ package com.jozufozu.flywheel.core.crumbling; import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterialGroup; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; -import com.jozufozu.flywheel.core.atlas.AtlasInfo; -import com.jozufozu.flywheel.core.atlas.SheetData; -import com.jozufozu.flywheel.util.RenderTextures; -import com.jozufozu.flywheel.util.TextureBinder; +import com.jozufozu.flywheel.util.Textures; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.math.Matrix4f; @@ -37,7 +34,7 @@ public class CrumblingGroup

extends InstancedMateria RenderSystem.setShaderTexture(0, renderTex); RenderSystem.setShaderTexture(4, breakingTex); - TextureBinder.bindActiveTextures(); + Textures.bindActiveTextures(); renderAll(viewProjection, camX, camY, camZ); CrumblingRenderer._currentLayer.clearRenderState(); @@ -45,11 +42,11 @@ public class CrumblingGroup

extends InstancedMateria private void updateAtlasSize() { - SheetData atlasData = AtlasInfo.getAtlasData(RenderTextures.getShaderTexture(0)); + AtlasInfo.SheetSize sheetSize = AtlasInfo.getSheetSize(Textures.getShaderTexture(0)); - if (atlasData != null) { - width = atlasData.width; - height = atlasData.height; + if (sheetSize != null) { + width = sheetSize.width(); + height = sheetSize.height(); } else { width = height = 256; } diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java index 8669ccef1..fddee3b91 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java @@ -2,7 +2,6 @@ package com.jozufozu.flywheel.core.crumbling; import static org.lwjgl.opengl.GL20.glUniform2f; -import com.jozufozu.flywheel.core.atlas.AtlasInfo; import com.jozufozu.flywheel.core.shader.WorldProgram; import net.minecraft.client.renderer.texture.TextureAtlas; diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index 57f75d84f..29efdc84d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -49,8 +49,8 @@ public class CrumblingRenderer { static { Pair, Lazy.KillSwitch> state = Lazy.ofKillable(State::new, State::kill); - STATE = state.getFirst(); - INVALIDATOR = state.getSecond(); + STATE = state.first(); + INVALIDATOR = state.second(); } public static void renderBreaking(RenderLayerEvent event) { diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java similarity index 66% rename from src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java rename to src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java index 854779e69..6dadbd2f3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java @@ -1,18 +1,18 @@ -package com.jozufozu.flywheel.core.model; +package com.jozufozu.flywheel.core.hardcoded; -import java.nio.ByteBuffer; import java.util.List; -import com.jozufozu.flywheel.core.vertex.PosNormalTexReader; +import com.jozufozu.flywheel.core.model.Model; +import com.jozufozu.flywheel.core.vertex.VertexList; +import com.jozufozu.flywheel.core.vertex.PosTexNormalVertexListUnsafe; import com.jozufozu.flywheel.core.vertex.PosTexNormalWriter; -import com.jozufozu.flywheel.util.ModelReader; import com.mojang.blaze3d.platform.MemoryTracker; public class ModelPart implements Model { private final int vertices; private final String name; - private final PosNormalTexReader reader; + private final PosTexNormalVertexListUnsafe reader; public ModelPart(List cuboids, String name) { this.name = name; @@ -25,13 +25,12 @@ public class ModelPart implements Model { this.vertices = vertices; } - ByteBuffer buffer = MemoryTracker.create(size()); - PosTexNormalWriter writer = new PosTexNormalWriter(buffer); + PosTexNormalWriter writer = new PosTexNormalWriter(MemoryTracker.create(size())); for (PartBuilder.CuboidBuilder cuboid : cuboids) { cuboid.buffer(writer); } - reader = new PosNormalTexReader(buffer, vertices); + reader = writer.intoReader(); } public static PartBuilder builder(String name, int sizeU, int sizeV) { @@ -49,7 +48,7 @@ public class ModelPart implements Model { } @Override - public ModelReader getReader() { + public VertexList getReader() { return reader; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java similarity index 99% rename from src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java rename to src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java index f96ba4cab..b5d7a503a 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.core.model; +package com.jozufozu.flywheel.core.hardcoded; import java.util.ArrayList; import java.util.EnumSet; diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java index 55bef78a8..b2f7090fd 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java @@ -3,9 +3,10 @@ package com.jozufozu.flywheel.core.materials.model; import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructWriter; +import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; +import com.jozufozu.flywheel.backend.gl.attrib.MatrixAttributes; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.Programs; import com.jozufozu.flywheel.core.model.ModelTransformer; @@ -13,6 +14,11 @@ import net.minecraft.resources.ResourceLocation; public class ModelType implements Instanced, Batched { + public static final VertexFormat FORMAT = VertexFormat.builder() + .addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA) + .addAttributes(MatrixAttributes.MAT4, MatrixAttributes.MAT3) + .build(); + @Override public ModelData create() { return new ModelData(); @@ -20,7 +26,7 @@ public class ModelType implements Instanced, Batched { @Override public VertexFormat format() { - return Formats.TRANSFORMED; + return FORMAT; } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedData.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedData.java index d819870f2..1971a3a05 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedData.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedData.java @@ -1,7 +1,6 @@ package com.jozufozu.flywheel.core.materials.oriented; import com.jozufozu.flywheel.core.materials.BasicData; -import com.jozufozu.flywheel.util.vec.Vec3; import com.mojang.math.Quaternion; import com.mojang.math.Vector3f; @@ -36,11 +35,6 @@ public class OrientedData extends BasicData { return this; } - - public OrientedData nudge(Vec3 pos) { - return nudge(pos.getX(), pos.getY(), pos.getZ()); - } - public OrientedData nudge(float x, float y, float z) { this.posX += x; this.posY += y; @@ -57,10 +51,6 @@ public class OrientedData extends BasicData { return setPosition((float) pos.x(), (float) pos.y(), (float) pos.z()); } - public OrientedData setPivot(Vec3 pos) { - return setPivot(pos.getX(), pos.getY(), pos.getZ()); - } - public OrientedData setPivot(float x, float y, float z) { this.pivotX = x; this.pivotY = y; diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java index 2bd2195ae..4bd6d9c52 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java @@ -3,9 +3,9 @@ package com.jozufozu.flywheel.core.materials.oriented; import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructWriter; +import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.Programs; import com.jozufozu.flywheel.core.model.ModelTransformer; import com.mojang.math.Quaternion; @@ -14,6 +14,11 @@ import net.minecraft.resources.ResourceLocation; public class OrientedType implements Instanced, Batched { + public static final VertexFormat FORMAT = VertexFormat.builder() + .addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA) + .addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION) + .build(); + @Override public OrientedData create() { return new OrientedData(); @@ -21,7 +26,7 @@ public class OrientedType implements Instanced, Batched rotateToFace(Direction facing) { + return () -> { + PoseStack stack = new PoseStack(); + TransformStack.cast(stack) + .centre() + .rotateToFace(facing) + .unCentre(); + return stack; + }; + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Readable.java b/src/main/java/com/jozufozu/flywheel/core/model/Readable.java deleted file mode 100644 index 1108ce02d..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/model/Readable.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.jozufozu.flywheel.core.model; - -import com.mojang.math.Vector3f; - -import net.minecraft.core.Direction; - -public class Readable { - public static class ModelBox { - private final TexturedQuad[] quads; - public final float posX1; - public final float posY1; - public final float posZ1; - public final float posX2; - public final float posY2; - public final float posZ2; - - public ModelBox(int texOffU, int texOffV, float posX1, float posY1, float posZ1, float sizeX, float sizeY, float sizeZ, float growX, float growY, float growZ, boolean mirror, float texWidth, float texHeight) { - this.posX1 = posX1; - this.posY1 = posY1; - this.posZ1 = posZ1; - this.posX2 = posX1 + sizeX; - this.posY2 = posY1 + sizeY; - this.posZ2 = posZ1 + sizeZ; - this.quads = new TexturedQuad[6]; - float posX2 = posX1 + sizeX; - float posY2 = posY1 + sizeY; - float posZ2 = posZ1 + sizeZ; - posX1 = posX1 - growX; - posY1 = posY1 - growY; - posZ1 = posZ1 - growZ; - posX2 = posX2 + growX; - posY2 = posY2 + growY; - posZ2 = posZ2 + growZ; - if (mirror) { - float tmp = posX2; - posX2 = posX1; - posX1 = tmp; - } - - PositionTextureVertex lll = new PositionTextureVertex(posX1, posY1, posZ1, 0.0F, 0.0F); - PositionTextureVertex hll = new PositionTextureVertex(posX2, posY1, posZ1, 0.0F, 8.0F); - PositionTextureVertex hhl = new PositionTextureVertex(posX2, posY2, posZ1, 8.0F, 8.0F); - PositionTextureVertex lhl = new PositionTextureVertex(posX1, posY2, posZ1, 8.0F, 0.0F); - PositionTextureVertex llh = new PositionTextureVertex(posX1, posY1, posZ2, 0.0F, 0.0F); - PositionTextureVertex hlh = new PositionTextureVertex(posX2, posY1, posZ2, 0.0F, 8.0F); - PositionTextureVertex hhh = new PositionTextureVertex(posX2, posY2, posZ2, 8.0F, 8.0F); - PositionTextureVertex lhh = new PositionTextureVertex(posX1, posY2, posZ2, 8.0F, 0.0F); - float f4 = (float)texOffU; - float f5 = (float)texOffU + sizeZ; - float f6 = (float)texOffU + sizeZ + sizeX; - float f7 = (float)texOffU + sizeZ + sizeX + sizeX; - float f8 = (float)texOffU + sizeZ + sizeX + sizeZ; - float f9 = (float)texOffU + sizeZ + sizeX + sizeZ + sizeX; - float f10 = (float)texOffV; - float f11 = (float)texOffV + sizeZ; - float f12 = (float)texOffV + sizeZ + sizeY; - this.quads[2] = new TexturedQuad(new PositionTextureVertex[]{hlh, llh, lll, hll}, f5, f10, f6, f11, texWidth, texHeight, mirror, Direction.DOWN); - this.quads[3] = new TexturedQuad(new PositionTextureVertex[]{hhl, lhl, lhh, hhh}, f6, f11, f7, f10, texWidth, texHeight, mirror, Direction.UP); - this.quads[1] = new TexturedQuad(new PositionTextureVertex[]{lll, llh, lhh, lhl}, f4, f11, f5, f12, texWidth, texHeight, mirror, Direction.WEST); - this.quads[4] = new TexturedQuad(new PositionTextureVertex[]{hll, lll, lhl, hhl}, f5, f11, f6, f12, texWidth, texHeight, mirror, Direction.NORTH); - this.quads[0] = new TexturedQuad(new PositionTextureVertex[]{hlh, hll, hhl, hhh}, f6, f11, f8, f12, texWidth, texHeight, mirror, Direction.EAST); - this.quads[5] = new TexturedQuad(new PositionTextureVertex[]{llh, hlh, hhh, lhh}, f8, f11, f9, f12, texWidth, texHeight, mirror, Direction.SOUTH); - } - } - - public static class PositionTextureVertex { - public final float x; - public final float y; - public final float z; - public final float u; - public final float v; - - public PositionTextureVertex(float x, float y, float z) { - this(x, y, z, 0, 0); - } - - public PositionTextureVertex(float x, float y, float z, float u, float v) { - this.x = x; - this.y = y; - this.z = z; - this.u = u; - this.v = v; - } - - public PositionTextureVertex setTexturePosition(float u, float v) { - return new PositionTextureVertex(x, y, z, u, v); - } - } - - public static class TexturedQuad { - public final PositionTextureVertex[] vertices; - public final Vector3f normal; - - public TexturedQuad(PositionTextureVertex[] vertices, float minU, float minV, float maxU, float maxV, float texWidth, float texHeight, boolean p_i225951_8_, Direction p_i225951_9_) { - this.vertices = vertices; - float w = 0.0F / texWidth; - float h = 0.0F / texHeight; - vertices[0] = vertices[0].setTexturePosition(maxU / texWidth - w, minV / texHeight + h); - vertices[1] = vertices[1].setTexturePosition(minU / texWidth + w, minV / texHeight + h); - vertices[2] = vertices[2].setTexturePosition(minU / texWidth + w, maxV / texHeight - h); - vertices[3] = vertices[3].setTexturePosition(maxU / texWidth - w, maxV / texHeight - h); - if (p_i225951_8_) { - int i = vertices.length; - - for(int j = 0; j < i / 2; ++j) { - PositionTextureVertex modelrenderer$positiontexturevertex = vertices[j]; - vertices[j] = vertices[i - 1 - j]; - vertices[i - 1 - j] = modelrenderer$positiontexturevertex; - } - } - - this.normal = p_i225951_9_.step(); - if (p_i225951_8_) { - this.normal.mul(-1.0F, 1.0F, 1.0F); - } - - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java b/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java deleted file mode 100644 index c1ea4957c..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.jozufozu.flywheel.core.model; - -import static com.jozufozu.flywheel.util.RenderMath.nb; - -import java.nio.ByteBuffer; - -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.mojang.blaze3d.vertex.VertexConsumer; - -public class VecBufferWriter implements VertexConsumer { - - private final VecBuffer buffer; - - public VecBufferWriter(ByteBuffer buffer) { - this.buffer = new VecBuffer(buffer); - } - - public VecBufferWriter(VecBuffer buffer) { - this.buffer = buffer; - } - - @Override - public VertexConsumer vertex(double v, double v1, double v2) { - buffer.putVec3((float) v, (float) v1, (float) v2); - return this; - } - - @Override - public VertexConsumer color(int i, int i1, int i2, int i3) { - buffer.putColor(i, i1, i2, i3); - return this; - } - - @Override - public VertexConsumer uv(float v, float v1) { - buffer.putVec2(v, v1); - return this; - } - - @Override - public VertexConsumer overlayCoords(int i, int i1) { - return this; - } - - @Override - public VertexConsumer uv2(int i, int i1) { - buffer.putVec2((byte) i, (byte) i1); - return this; - } - - @Override - public VertexConsumer normal(float v, float v1, float v2) { - buffer.putVec3(nb(v), nb(v1), nb(v2)); - return this; - } - - @Override - public void endVertex() { - - } - - @Override - public void defaultColor(int i, int i1, int i2, int i3) { - - } - - @Override - public void unsetDefaultColor() { - - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java index 4d61f4c80..e390bdbc5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java @@ -2,9 +2,10 @@ package com.jozufozu.flywheel.core.model; import java.util.Collection; +import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.core.vertex.VertexList; import com.jozufozu.flywheel.core.vertex.VertexType; -import com.jozufozu.flywheel.util.UnsafeBlockFormatReader; -import com.jozufozu.flywheel.util.ModelReader; +import com.jozufozu.flywheel.core.vertex.BlockVertexListUnsafe; import net.minecraft.client.renderer.RenderType; import net.minecraft.world.level.BlockAndTintGetter; @@ -12,11 +13,11 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp public class WorldModel implements Model { - private final ModelReader reader; + private final VertexList reader; private final String name; public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection blocks, String name) { - reader = new UnsafeBlockFormatReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); + reader = new BlockVertexListUnsafe(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); this.name = name; } @@ -27,7 +28,7 @@ public class WorldModel implements Model { @Override public VertexType getType() { - return BlockType.INSTANCE; + return Formats.BLOCK; } @Override @@ -36,7 +37,7 @@ public class WorldModel implements Model { } @Override - public ModelReader getReader() { + public VertexList getReader() { return reader; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java b/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java index d0591c29d..12bc659ad 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java @@ -21,8 +21,8 @@ public class GameStateProgram

implements ContextAwareProgra @Override public P get() { for (Pair variant : variants) { - if (variant.getFirst() - .isMet()) return variant.getSecond(); + if (variant.first() + .isMet()) return variant.second(); } return fallback; @@ -31,7 +31,7 @@ public class GameStateProgram

implements ContextAwareProgra @Override public void delete() { for (Pair variant : variants) { - variant.getSecond() + variant.second() .delete(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java new file mode 100644 index 000000000..2264443e9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java @@ -0,0 +1,71 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.util.RenderMath; + +import net.minecraft.client.renderer.LightTexture; + +public class BlockVertex implements VertexType { + + public static final VertexFormat FORMAT = VertexFormat.builder() + .addAttributes(CommonAttributes.VEC3, + CommonAttributes.UV, + CommonAttributes.RGBA, + CommonAttributes.LIGHT, + CommonAttributes.NORMAL) + .build(); + + @Override + public VertexFormat getFormat() { + return FORMAT; + } + + @Override + public void copyInto(ByteBuffer buffer, VertexList reader) { + int stride = getStride(); + long addr = MemoryUtil.memAddress(buffer); + + int vertexCount = reader.getVertexCount(); + for (int i = 0; i < vertexCount; i++) { + float x = reader.getX(i); + float y = reader.getY(i); + float z = reader.getZ(i); + + float xN = reader.getNX(i); + float yN = reader.getNY(i); + float zN = reader.getNZ(i); + + float u = reader.getU(i); + float v = reader.getV(i); + + byte r = reader.getR(i); + byte g = reader.getG(i); + byte b = reader.getB(i); + byte a = reader.getA(i); + + int light = reader.getLight(i); + + MemoryUtil.memPutFloat(addr, x); + MemoryUtil.memPutFloat(addr + 4, y); + MemoryUtil.memPutFloat(addr + 8, z); + MemoryUtil.memPutFloat(addr + 12, u); + MemoryUtil.memPutFloat(addr + 16, v); + MemoryUtil.memPutByte(addr + 20, r); + MemoryUtil.memPutByte(addr + 21, g); + MemoryUtil.memPutByte(addr + 22, b); + MemoryUtil.memPutByte(addr + 23, a); + MemoryUtil.memPutByte(addr + 24, (byte) (LightTexture.block(light) << 4)); + MemoryUtil.memPutByte(addr + 25, (byte) (LightTexture.sky(light) << 4)); + MemoryUtil.memPutByte(addr + 26, RenderMath.nb(xN)); + MemoryUtil.memPutByte(addr + 27, RenderMath.nb(yN)); + MemoryUtil.memPutByte(addr + 28, RenderMath.nb(zN)); + + addr += stride; + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java similarity index 90% rename from src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java rename to src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java index 2372d0036..a316d26e6 100644 --- a/src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java @@ -1,17 +1,18 @@ -package com.jozufozu.flywheel.util; +package com.jozufozu.flywheel.core.vertex; import java.nio.ByteBuffer; +import com.jozufozu.flywheel.util.RenderMath; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.datafixers.util.Pair; -public class BlockFormatReader implements ModelReader { +public class BlockVertexList implements VertexList { private final ByteBuffer buffer; private final int vertexCount; private final int stride; - public BlockFormatReader(BufferBuilder builder) { + public BlockVertexList(BufferBuilder builder) { Pair data = builder.popNextBuffer(); buffer = data.getSecond(); diff --git a/src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java similarity index 91% rename from src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java rename to src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java index a81ca4205..d47cf6839 100644 --- a/src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java @@ -1,20 +1,21 @@ -package com.jozufozu.flywheel.util; +package com.jozufozu.flywheel.core.vertex; import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; +import com.jozufozu.flywheel.util.RenderMath; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.datafixers.util.Pair; -public class UnsafeBlockFormatReader implements ModelReader { +public class BlockVertexListUnsafe implements VertexList { private final int vertexCount; private final int stride; private final long base; - public UnsafeBlockFormatReader(BufferBuilder builder) { + public BlockVertexListUnsafe(BufferBuilder builder) { VertexFormat vertexFormat = builder.getVertexFormat(); Pair data = builder.popNextBuffer(); this.base = MemoryUtil.memAddress(data.getSecond()); diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java similarity index 63% rename from src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java rename to src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java index eb2396848..590c7d82e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java @@ -2,21 +2,22 @@ package com.jozufozu.flywheel.core.vertex; import java.nio.ByteBuffer; +import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.core.Formats; -import com.jozufozu.flywheel.util.ModelReader; -public class PosNormalTexType implements VertexType { +public class PosTexNormalVertex implements VertexType { - public static final PosNormalTexType INSTANCE = new PosNormalTexType(); + public static final VertexFormat FORMAT = VertexFormat.builder() + .addAttributes(CommonAttributes.VEC3, CommonAttributes.UV, CommonAttributes.NORMAL) + .build(); @Override public VertexFormat getFormat() { - return Formats.UNLIT_MODEL; + return FORMAT; } @Override - public void copyInto(ByteBuffer buffer, ModelReader reader) { + public void copyInto(ByteBuffer buffer, VertexList reader) { PosTexNormalWriter writer = new PosTexNormalWriter(buffer); int vertexCount = reader.getVertexCount(); diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java similarity index 88% rename from src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java rename to src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java index f5c745148..801c0e56b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java @@ -4,18 +4,15 @@ import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; -import com.jozufozu.flywheel.util.ModelReader; import com.jozufozu.flywheel.util.RenderMath; -import net.minecraft.client.renderer.LightTexture; - -public class PosNormalTexReader implements ModelReader { +public class PosTexNormalVertexListUnsafe implements VertexList { private final ByteBuffer buffer; private final int vertexCount; private final long base; - public PosNormalTexReader(ByteBuffer buffer, int vertexCount) { + public PosTexNormalVertexListUnsafe(ByteBuffer buffer, int vertexCount) { this.buffer = buffer; this.vertexCount = vertexCount; this.base = MemoryUtil.memAddress(buffer); diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java index a0a7aa1f7..c04bdd651 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java @@ -8,11 +8,13 @@ import com.jozufozu.flywheel.util.RenderMath; public class PosTexNormalWriter { + private final ByteBuffer buffer; private long addr; private int vertexCount; public PosTexNormalWriter(ByteBuffer buffer) { + this.buffer = buffer; addr = MemoryUtil.memAddress(buffer); } @@ -33,4 +35,8 @@ public class PosTexNormalWriter { public int getVertexCount() { return vertexCount; } + + public PosTexNormalVertexListUnsafe intoReader() { + return new PosTexNormalVertexListUnsafe(buffer, vertexCount); + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/ModelReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java similarity index 85% rename from src/main/java/com/jozufozu/flywheel/util/ModelReader.java rename to src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java index c1dda5f8b..a87df6315 100644 --- a/src/main/java/com/jozufozu/flywheel/util/ModelReader.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.util; +package com.jozufozu.flywheel.core.vertex; -public interface ModelReader { +public interface VertexList { float getX(int index); float getY(int index); diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java index 24fd80ba1..d58f1b076 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java @@ -3,13 +3,12 @@ package com.jozufozu.flywheel.core.vertex; import java.nio.ByteBuffer; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.util.ModelReader; public interface VertexType { VertexFormat getFormat(); - void copyInto(ByteBuffer buffer, ModelReader reader); + void copyInto(ByteBuffer buffer, VertexList reader); default int getStride() { return getFormat().getStride(); diff --git a/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java b/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java index 68985220b..43d42d90a 100644 --- a/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java +++ b/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java @@ -23,6 +23,8 @@ import org.lwjgl.opengl.GL30; import com.jozufozu.flywheel.backend.gl.GlTexture; import com.jozufozu.flywheel.backend.gl.GlTextureUnit; +import com.jozufozu.flywheel.util.box.GridAlignedBB; +import com.jozufozu.flywheel.util.box.ImmutableBox; import net.minecraft.world.level.LightLayer; diff --git a/src/main/java/com/jozufozu/flywheel/light/LightListener.java b/src/main/java/com/jozufozu/flywheel/light/LightListener.java index 9037d3b1f..396ae6bbd 100644 --- a/src/main/java/com/jozufozu/flywheel/light/LightListener.java +++ b/src/main/java/com/jozufozu/flywheel/light/LightListener.java @@ -1,5 +1,8 @@ package com.jozufozu.flywheel.light; +import com.jozufozu.flywheel.util.box.GridAlignedBB; +import com.jozufozu.flywheel.util.box.ImmutableBox; + import net.minecraft.world.level.LightLayer; public interface LightListener { diff --git a/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java b/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java index 26404ab2a..cf6a8d491 100644 --- a/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java +++ b/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java @@ -6,6 +6,8 @@ import java.util.Set; import java.util.stream.Stream; import com.jozufozu.flywheel.util.WeakHashSet; +import com.jozufozu.flywheel.util.box.GridAlignedBB; +import com.jozufozu.flywheel.util.box.ImmutableBox; import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/jozufozu/flywheel/light/LightVolume.java b/src/main/java/com/jozufozu/flywheel/light/LightVolume.java index 2924277b3..b5a830f86 100644 --- a/src/main/java/com/jozufozu/flywheel/light/LightVolume.java +++ b/src/main/java/com/jozufozu/flywheel/light/LightVolume.java @@ -4,6 +4,9 @@ import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; +import com.jozufozu.flywheel.util.box.GridAlignedBB; +import com.jozufozu.flywheel.util.box.ImmutableBox; + import net.minecraft.core.BlockPos; import net.minecraft.world.level.LightLayer; diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderTexturesMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderTexturesMixin.java index 5e38c5ea1..083d2050f 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/RenderTexturesMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderTexturesMixin.java @@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import com.jozufozu.flywheel.util.RenderTextures; +import com.jozufozu.flywheel.util.Textures; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.resources.ResourceLocation; @@ -15,6 +15,6 @@ public class RenderTexturesMixin { @Inject(method = "_setShaderTexture(ILnet/minecraft/resources/ResourceLocation;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/AbstractTexture;getId()I")) private static void storeTextureLoc(int pShaderTexture, ResourceLocation pTextureId, CallbackInfo ci) { - RenderTextures._setShaderTexture(pShaderTexture, pTextureId); + Textures._setShaderTexture(pShaderTexture, pTextureId); } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/atlas/AtlasDataMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/atlas/AtlasDataMixin.java index 9a9cd1dc2..0df35f618 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/atlas/AtlasDataMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/atlas/AtlasDataMixin.java @@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import com.jozufozu.flywheel.core.atlas.AtlasInfo; +import com.jozufozu.flywheel.core.crumbling.AtlasInfo; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.resources.ResourceLocation; @@ -23,10 +23,6 @@ public abstract class AtlasDataMixin { @Inject(method = "prepareToStitch", at = @At("RETURN")) public void stealAtlasData(ResourceManager resourceManager, Stream locationStream, ProfilerFiller profiler, int mipMapLevels, CallbackInfoReturnable cir) { - TextureAtlas.Preparations value = cir.getReturnValue(); - - SheetDataAccessor dataAccessor = (SheetDataAccessor) value; - - AtlasInfo.setAtlasData(location(), dataAccessor); + AtlasInfo._setAtlasData(location(), (SheetDataAccessor) cir.getReturnValue()); } } diff --git a/src/main/java/com/jozufozu/flywheel/util/AngleHelper.java b/src/main/java/com/jozufozu/flywheel/util/AngleHelper.java deleted file mode 100644 index a89f8bf87..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/AngleHelper.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.jozufozu.flywheel.util; - -import net.minecraft.core.Direction; -import net.minecraft.core.Direction.Axis; - -public class AngleHelper { - - public static float horizontalAngle(Direction facing) { - if (facing.getAxis() - .isVertical()) { - return 0; - } - float angle = facing.toYRot(); - if (facing.getAxis() == Axis.X) angle = -angle; - return angle; - } - - public static float verticalAngle(Direction facing) { - return facing == Direction.UP ? -90 : facing == Direction.DOWN ? 90 : 0; - } - - public static float rad(double angle) { - if (angle == 0) return 0; - return (float) (angle / 180 * Math.PI); - } - - public static float deg(double angle) { - if (angle == 0) return 0; - return (float) (angle * 180 / Math.PI); - } - - public static float angleLerp(double pct, double current, double target) { - return (float) (current + getShortestAngleDiff(current, target) * pct); - } - - public static float getShortestAngleDiff(double current, double target) { - current = current % 360; - target = target % 360; - return (float) (((((target - current) % 360) + 540) % 360) - 180); - } - -} diff --git a/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java b/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java index c16da14d8..20ed918df 100644 --- a/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java +++ b/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java @@ -4,22 +4,22 @@ import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor; import net.minecraft.client.Minecraft; +/** + * Static access to tick-count and partialTick time, accounting for pausing. + */ public class AnimationTickHolder { + // Wrap around every 24 hours to maintain floating point accuracy. + private static final int wrappingInterval = 1_728_000; private static int ticks; private static int paused_ticks; - public static void reset() { - ticks = 0; - paused_ticks = 0; - } - public static void tick() { if (!Minecraft.getInstance() .isPaused()) { - ticks = (ticks + 1) % 1_728_000; // wrap around every 24 hours so we maintain enough floating point precision + ticks = (ticks + 1) % wrappingInterval; } else { - paused_ticks = (paused_ticks + 1) % 1_728_000; + paused_ticks = (paused_ticks + 1) % wrappingInterval; } } @@ -39,4 +39,10 @@ public class AnimationTickHolder { Minecraft mc = Minecraft.getInstance(); return (mc.isPaused() ? ((PausedPartialTickAccessor) mc).flywheel$getPartialTicksPaused() : mc.getFrameTime()); } + + // Unused but might be useful for debugging. + public static void _reset() { + ticks = 0; + paused_ticks = 0; + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java b/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java index eb1c7aa89..b50fe47a3 100644 --- a/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.util; import org.lwjgl.opengl.GL20; +// TODO: move this functionality into GlVertexArray and track it public class AttribUtil { public static void enableArrays(int count) { diff --git a/src/main/java/com/jozufozu/flywheel/util/Pair.java b/src/main/java/com/jozufozu/flywheel/util/Pair.java index ac3a10d83..6b3a48d7d 100644 --- a/src/main/java/com/jozufozu/flywheel/util/Pair.java +++ b/src/main/java/com/jozufozu/flywheel/util/Pair.java @@ -2,34 +2,14 @@ package com.jozufozu.flywheel.util; import java.util.Objects; -public class Pair { - - F first; - S second; - - protected Pair(F first, S second) { - this.first = first; - this.second = second; - } +public record Pair(F first, S second) { public static Pair of(F first, S second) { return new Pair<>(first, second); } - public F getFirst() { - return first; - } - - public S getSecond() { - return second; - } - - public void setFirst(F first) { - this.first = first; - } - - public void setSecond(S second) { - this.second = second; + public Pair swap() { + return Pair.of(second, first); } public Pair copy() { @@ -39,8 +19,7 @@ public class Pair { @Override public boolean equals(final Object obj) { if (obj == this) return true; - if (obj instanceof Pair) { - final Pair other = (Pair) obj; + if (obj instanceof final Pair other) { return Objects.equals(first, other.first) && Objects.equals(second, other.second); } return false; @@ -51,17 +30,13 @@ public class Pair { return (nullHash(first) * 31) ^ nullHash(second); } - int nullHash(Object o) { - return o == null ? 0 : o.hashCode(); - } - @Override public String toString() { return "(" + first + ", " + second + ")"; } - public Pair swap() { - return Pair.of(second, first); + static int nullHash(Object o) { + return o == null ? 0 : o.hashCode(); } } diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderMath.java b/src/main/java/com/jozufozu/flywheel/util/RenderMath.java index 3b891d683..77321641b 100644 --- a/src/main/java/com/jozufozu/flywheel/util/RenderMath.java +++ b/src/main/java/com/jozufozu/flywheel/util/RenderMath.java @@ -29,4 +29,42 @@ public class RenderMath { public static byte unb(float f) { return (byte) Math.floor(f * 255); } + + public static int nextPowerOf2(int a) { + int h = Integer.highestOneBit(a); + return (h == a) ? h : (h << 1); + } + + public static boolean isPowerOf2(int n) { + int b = n & (n - 1); + return b == 0 && n != 0; + } + + public static double lengthSqr(double x, double y, double z) { + return x * x + y * y + z * z; + } + + public static double length(double x, double y, double z) { + return Math.sqrt(lengthSqr(x, y, z)); + } + + public static float rad(double angle) { + if (angle == 0) return 0; + return (float) (angle / 180 * Math.PI); + } + + public static float deg(double angle) { + if (angle == 0) return 0; + return (float) (angle * 180 / Math.PI); + } + + public static float angleLerp(double pct, double current, double target) { + return (float) (current + getShortestAngleDiff(current, target) * pct); + } + + public static float getShortestAngleDiff(double current, double target) { + current = current % 360; + target = target % 360; + return (float) (((((target - current) % 360) + 540) % 360) - 180); + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderUtil.java b/src/main/java/com/jozufozu/flywheel/util/RenderUtil.java deleted file mode 100644 index 4897d7888..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/RenderUtil.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jozufozu.flywheel.util; - -import java.util.function.Supplier; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Matrix4f; -import com.mojang.math.Vector3f; - -import net.minecraft.core.Direction; - -public class RenderUtil { - - private static final Matrix4f IDENTITY = new Matrix4f(); - static { - IDENTITY.setIdentity(); - } - - public static Matrix4f getIdentity() { - return IDENTITY; - } - - public static Matrix4f copyIdentity() { - return IDENTITY.copy(); - } - - public static int nextPowerOf2(int a) { - int h = Integer.highestOneBit(a); - return (h == a) ? h : (h << 1); - } - - public static boolean isPowerOf2(int n) { - int b = n & (n - 1); - return b == 0 && n != 0; - } - - public static double lengthSqr(double x, double y, double z) { - return x * x + y * y + z * z; - } - - public static double length(double x, double y, double z) { - return Math.sqrt(lengthSqr(x, y, z)); - } - - public static Supplier rotateToFace(Direction facing) { - return () -> { - PoseStack stack = new PoseStack(); - // MatrixStacker.of(stack) - // .centre() - // .rotateY(AngleHelper.horizontalAngle(facing)) - // .rotateX(AngleHelper.verticalAngle(facing)) - // .unCentre(); - stack.last() - .pose() - .setTranslation(0.5f, 0.5f, 0.5f); - stack.mulPose(Vector3f.YP.rotationDegrees(AngleHelper.horizontalAngle(facing))); - stack.mulPose(Vector3f.XP.rotationDegrees(AngleHelper.verticalAngle(facing))); - stack.translate(-0.5f, -0.5f, -0.5f); - return stack; - }; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java b/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java deleted file mode 100644 index dd1b4d70a..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.jozufozu.flywheel.util; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.channels.ReadableByteChannel; - -import org.lwjgl.system.MemoryUtil; - -public class StreamUtil { - public static String readToString(InputStream is) { - ByteBuffer bytebuffer = null; - - try { - bytebuffer = readToBuffer(is); - int i = bytebuffer.position(); - ((Buffer) bytebuffer).rewind(); - return MemoryUtil.memASCII(bytebuffer, i); - } catch (IOException e) { - - } finally { - if (bytebuffer != null) { - MemoryUtil.memFree(bytebuffer); - } - - } - - return null; - } - - public static ByteBuffer readToBuffer(InputStream is) throws IOException { - ByteBuffer bytebuffer; - if (is instanceof FileInputStream) { - FileInputStream fileinputstream = (FileInputStream) is; - FileChannel filechannel = fileinputstream.getChannel(); - bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1); - - while (filechannel.read(bytebuffer) != -1) { - } - } else { - bytebuffer = MemoryUtil.memAlloc(8192); - ReadableByteChannel readablebytechannel = Channels.newChannel(is); - - while (readablebytechannel.read(bytebuffer) != -1) { - if (bytebuffer.remaining() == 0) { - bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2); - } - } - } - - return bytebuffer; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java index b4ee5a638..9a1d5d2e9 100644 --- a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java @@ -1,8 +1,18 @@ package com.jozufozu.flywheel.util; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; import java.util.Arrays; import java.util.stream.Collectors; +import org.lwjgl.system.MemoryUtil; + public class StringUtil { public static String args(String functionName, Object... args) { @@ -20,4 +30,52 @@ public class StringUtil { } return value.substring(0, len); } + + public static String readToString(InputStream is) { + ByteBuffer bytebuffer = null; + + try { + bytebuffer = readToBuffer(is); + int i = bytebuffer.position(); + ((Buffer) bytebuffer).rewind(); + return MemoryUtil.memASCII(bytebuffer, i); + } catch (IOException ignored) { + } finally { + if (bytebuffer != null) { + MemoryUtil.memFree(bytebuffer); + } + + } + + return null; + } + + public static ByteBuffer readToBuffer(InputStream is) throws IOException { + if (is instanceof FileInputStream fileinputstream) { + return readFileInputStream(fileinputstream); + } else { + return readInputStream(is); + } + } + + private static ByteBuffer readInputStream(InputStream is) throws IOException { + ByteBuffer bytebuffer = MemoryUtil.memAlloc(8192); + ReadableByteChannel readablebytechannel = Channels.newChannel(is); + + while (readablebytechannel.read(bytebuffer) != -1) { + if (bytebuffer.remaining() == 0) { + bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2); + } + } + return bytebuffer; + } + + private static ByteBuffer readFileInputStream(FileInputStream fileinputstream) throws IOException { + FileChannel filechannel = fileinputstream.getChannel(); + ByteBuffer bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1); + + while (filechannel.read(bytebuffer) != -1) { + } + return bytebuffer; + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/TextureBinder.java b/src/main/java/com/jozufozu/flywheel/util/TextureBinder.java index ba9c4f64e..e90435078 100644 --- a/src/main/java/com/jozufozu/flywheel/util/TextureBinder.java +++ b/src/main/java/com/jozufozu/flywheel/util/TextureBinder.java @@ -1,9 +1,5 @@ package com.jozufozu.flywheel.util; -import org.lwjgl.opengl.GL32; - -import com.mojang.blaze3d.systems.RenderSystem; - import net.minecraft.client.renderer.RenderType; /** @@ -16,14 +12,4 @@ import net.minecraft.client.renderer.RenderType; */ public class TextureBinder { - /** - * Call this after calling {@link RenderType#setupRenderState()}. - */ - public static void bindActiveTextures() { - for (int i = 0; i < 12; i++) { - int shaderTexture = RenderSystem.getShaderTexture(i); - RenderSystem.activeTexture(GL32.GL_TEXTURE0 + i); - RenderSystem.bindTexture(shaderTexture); - } - } } diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderTextures.java b/src/main/java/com/jozufozu/flywheel/util/Textures.java similarity index 58% rename from src/main/java/com/jozufozu/flywheel/util/RenderTextures.java rename to src/main/java/com/jozufozu/flywheel/util/Textures.java index 8ea3f12dc..f648e7cde 100644 --- a/src/main/java/com/jozufozu/flywheel/util/RenderTextures.java +++ b/src/main/java/com/jozufozu/flywheel/util/Textures.java @@ -2,6 +2,11 @@ package com.jozufozu.flywheel.util; import javax.annotation.Nullable; +import org.lwjgl.opengl.GL32; + +import com.mojang.blaze3d.systems.RenderSystem; + +import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; /** @@ -11,7 +16,7 @@ import net.minecraft.resources.ResourceLocation; * Works with {@link com.jozufozu.flywheel.mixin.RenderTexturesMixin}. *

*/ -public class RenderTextures { +public class Textures { private static final ResourceLocation[] shaderTextures = new ResourceLocation[12]; @@ -23,4 +28,15 @@ public class RenderTextures { public static void _setShaderTexture(int pShaderTexture, ResourceLocation pTextureId) { shaderTextures[pShaderTexture] = pTextureId; } + + /** + * Call this after calling {@link RenderType#setupRenderState()}. + */ + public static void bindActiveTextures() { + for (int i = 0; i < 12; i++) { + int shaderTexture = RenderSystem.getShaderTexture(i); + RenderSystem.activeTexture(GL32.GL_TEXTURE0 + i); + RenderSystem.bindTexture(shaderTexture); + } + } } diff --git a/src/main/java/com/jozufozu/flywheel/light/CoordinateConsumer.java b/src/main/java/com/jozufozu/flywheel/util/box/CoordinateConsumer.java similarity index 71% rename from src/main/java/com/jozufozu/flywheel/light/CoordinateConsumer.java rename to src/main/java/com/jozufozu/flywheel/util/box/CoordinateConsumer.java index b6a64eb0d..f42c5a00b 100644 --- a/src/main/java/com/jozufozu/flywheel/light/CoordinateConsumer.java +++ b/src/main/java/com/jozufozu/flywheel/util/box/CoordinateConsumer.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.light; +package com.jozufozu.flywheel.util.box; @FunctionalInterface public interface CoordinateConsumer { diff --git a/src/main/java/com/jozufozu/flywheel/light/GridAlignedBB.java b/src/main/java/com/jozufozu/flywheel/util/box/GridAlignedBB.java similarity index 96% rename from src/main/java/com/jozufozu/flywheel/light/GridAlignedBB.java rename to src/main/java/com/jozufozu/flywheel/util/box/GridAlignedBB.java index e96f9fbc7..fa6925a36 100644 --- a/src/main/java/com/jozufozu/flywheel/light/GridAlignedBB.java +++ b/src/main/java/com/jozufozu/flywheel/util/box/GridAlignedBB.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.light; +package com.jozufozu.flywheel.util.box; -import com.jozufozu.flywheel.util.RenderUtil; +import com.jozufozu.flywheel.util.RenderMath; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -116,9 +116,9 @@ public class GridAlignedBB implements ImmutableBox { int sizeY = sizeY(); int sizeZ = sizeZ(); - int newSizeX = RenderUtil.nextPowerOf2(sizeX); - int newSizeY = RenderUtil.nextPowerOf2(sizeY); - int newSizeZ = RenderUtil.nextPowerOf2(sizeZ); + int newSizeX = RenderMath.nextPowerOf2(sizeX); + int newSizeY = RenderMath.nextPowerOf2(sizeY); + int newSizeZ = RenderMath.nextPowerOf2(sizeZ); int diffX = newSizeX - sizeX; int diffY = newSizeY - sizeY; @@ -136,9 +136,9 @@ public class GridAlignedBB implements ImmutableBox { * Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords. */ public void nextPowerOf2() { - int sizeX = RenderUtil.nextPowerOf2(sizeX()); - int sizeY = RenderUtil.nextPowerOf2(sizeY()); - int sizeZ = RenderUtil.nextPowerOf2(sizeZ()); + int sizeX = RenderMath.nextPowerOf2(sizeX()); + int sizeY = RenderMath.nextPowerOf2(sizeY()); + int sizeZ = RenderMath.nextPowerOf2(sizeZ()); maxX = minX + sizeX; maxY = minY + sizeY; diff --git a/src/main/java/com/jozufozu/flywheel/light/ImmutableBox.java b/src/main/java/com/jozufozu/flywheel/util/box/ImmutableBox.java similarity index 97% rename from src/main/java/com/jozufozu/flywheel/light/ImmutableBox.java rename to src/main/java/com/jozufozu/flywheel/util/box/ImmutableBox.java index 719966424..10a5c7a91 100644 --- a/src/main/java/com/jozufozu/flywheel/light/ImmutableBox.java +++ b/src/main/java/com/jozufozu/flywheel/util/box/ImmutableBox.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.light; +package com.jozufozu.flywheel.util.box; -import static com.jozufozu.flywheel.util.RenderUtil.isPowerOf2; +import static com.jozufozu.flywheel.util.RenderMath.isPowerOf2; import net.minecraft.world.phys.AABB; diff --git a/src/main/java/com/jozufozu/flywheel/util/vec/Vec3.java b/src/main/java/com/jozufozu/flywheel/util/vec/Vec3.java deleted file mode 100644 index 4bd8e309f..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/vec/Vec3.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.jozufozu.flywheel.util.vec; - -import com.mojang.math.Quaternion; -import com.mojang.math.Vector3f; - -public class Vec3 { - public static final Vec3 NEGATIVE_X = new Vec3(-1.0F, 0.0F, 0.0F); - public static final Vec3 POSITIVE_X = new Vec3(1.0F, 0.0F, 0.0F); - public static final Vec3 NEGATIVE_Y = new Vec3(0.0F, -1.0F, 0.0F); - public static final Vec3 POSITIVE_Y = new Vec3(0.0F, 1.0F, 0.0F); - public static final Vec3 NEGATIVE_Z = new Vec3(0.0F, 0.0F, -1.0F); - public static final Vec3 POSITIVE_Z = new Vec3(0.0F, 0.0F, 1.0F); - - private float x; - private float y; - private float z; - - public Vec3(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - } - - public float getX() { - return x; - } - - public float getY() { - return y; - } - - public float getZ() { - return z; - } - - public Vec3 multiply(Quaternion quat) { - Vec4 vec4 = new Vec4(this, 1f); - - vec4.multiply(quat); - - return set(vec4.getX(), vec4.getY(), vec4.getZ()); - } - - public Vec3 copy() { - return new Vec3(x, y, z); - } - - public Vector3f convert() { - return new Vector3f(x, y, z); - } - - public Vec3 set(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - return this; - } - - public Vec3 add(Vec3 v) { - return add(v.x, v.y, v.z); - } - - public Vec3 add(float x, float y, float z) { - this.x += x; - this.y += y; - this.z += z; - return this; - } - - public Vec3 sub(Vec3 v) { - return sub(v.x, v.y, v.z); - } - - public Vec3 sub(float x, float y, float z) { - this.x -= x; - this.y -= y; - this.z -= z; - return this; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/util/vec/Vec4.java b/src/main/java/com/jozufozu/flywheel/util/vec/Vec4.java deleted file mode 100644 index b4f5cb663..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/vec/Vec4.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jozufozu.flywheel.util.vec; - -import com.mojang.math.Quaternion; - -public class Vec4 { - - private float x; - private float y; - private float z; - private float w; - - public Vec4(float x, float y, float z, float w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - public Vec4(Vec3 vec3) { - this(vec3, 0); - } - - public Vec4(Vec3 vec3, float w) { - this.x = vec3.getX(); - this.y = vec3.getY(); - this.z = vec3.getZ(); - this.w = w; - } - - public Vec4 multiply(Quaternion quat) { - Quaternion quaternion = new Quaternion(quat); - quaternion.mul(new Quaternion(this.getX(), this.getY(), this.getZ(), 0.0F)); - Quaternion quaternion1 = new Quaternion(quat); - quaternion1.conj(); - quaternion.mul(quaternion1); - return set(quaternion.i(), quaternion.j(), quaternion.k(), this.getW()); - } - - public Vec3 xyz() { - return new Vec3(x, y, z); - } - - public float getX() { - return x; - } - - public float getY() { - return y; - } - - public float getZ() { - return z; - } - - public float getW() { - return w; - } - - public Vec4 set(float x, float y, float z, float w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - return this; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/BellInstance.java b/src/main/java/com/jozufozu/flywheel/vanilla/BellInstance.java index 5cfd8eb60..c66e41b63 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/BellInstance.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/BellInstance.java @@ -5,7 +5,7 @@ import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.materials.oriented.OrientedData; -import com.jozufozu.flywheel.core.model.ModelPart; +import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.util.AnimationTickHolder; import com.mojang.math.Quaternion; import com.mojang.math.Vector3f; diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/ChestInstance.java b/src/main/java/com/jozufozu/flywheel/vanilla/ChestInstance.java index 0dddeefb7..ab1acdebf 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/ChestInstance.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/ChestInstance.java @@ -10,7 +10,7 @@ import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.materials.model.ModelData; import com.jozufozu.flywheel.core.materials.oriented.OrientedData; -import com.jozufozu.flywheel.core.model.ModelPart; +import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.util.AnimationTickHolder; import com.mojang.math.Quaternion; import com.mojang.math.Vector3f; diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/MinecartInstance.java b/src/main/java/com/jozufozu/flywheel/vanilla/MinecartInstance.java index bf53ae248..4fe3116df 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/MinecartInstance.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/MinecartInstance.java @@ -7,7 +7,7 @@ import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance; import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.materials.model.ModelData; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.core.model.ModelPart; +import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.transform.MatrixTransformStack; import com.mojang.math.Vector3f; diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/ShulkerBoxInstance.java b/src/main/java/com/jozufozu/flywheel/vanilla/ShulkerBoxInstance.java index 1f3ecbfe1..a64e5b0ec 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/ShulkerBoxInstance.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/ShulkerBoxInstance.java @@ -5,7 +5,7 @@ import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.materials.model.ModelData; -import com.jozufozu.flywheel.core.model.ModelPart; +import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.transform.MatrixTransformStack; import com.mojang.math.Quaternion; From ede2ba8776e5ff29afda17a9671e01a2c9d4358d Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 22 Dec 2021 02:45:45 -0800 Subject: [PATCH 11/22] Finally safe vertex formats - true to false for Pepper - IBufferedModel -> BufferedModel - VertexFormat -> BufferLayout - Use ImmutableList in BufferLayout - LayoutItem naming consistency - Try to reduce usage of raw BufferLayouts - Move vertex interfaces to api package - #createWriter and #createReader in VertexType - Some documentation --- .../flywheel/api/struct/StructType.java | 8 +- .../{core => api}/vertex/VertexList.java | 11 ++- .../flywheel/api/vertex/VertexType.java | 42 +++++++++ .../flywheel/api/vertex/VertexWriter.java | 15 +++ .../attrib => api/vertex}/package-info.java | 2 +- .../jozufozu/flywheel/backend/Backend.java | 2 +- .../backend/gl/attrib/CommonAttributes.java | 21 ----- .../backend/gl/attrib/VertexFormat.java | 61 ------------- .../backend/instancing/AbstractInstancer.java | 8 +- .../instancing/instancing/GPUInstancer.java | 13 +-- .../flywheel/backend/model/BufferedModel.java | 89 ++++-------------- .../backend/model/IBufferedModel.java | 36 -------- .../backend/model/ImmediateAllocator.java | 2 +- .../flywheel/backend/model/IndexedModel.java | 2 +- .../backend/model/ModelAllocator.java | 4 +- .../flywheel/backend/model/ModelPool.java | 28 +++--- .../flywheel/backend/model/ModelRenderer.java | 2 +- .../flywheel/backend/model/VBOModel.java | 91 +++++++++++++++++++ .../flywheel/backend/struct/BufferWriter.java | 7 +- .../flywheel/core/hardcoded/ModelPart.java | 10 +- .../flywheel/core/hardcoded/PartBuilder.java | 6 +- .../flywheel/core/hardcoded/package-info.java | 6 ++ .../flywheel/core/layout/BufferLayout.java | 74 +++++++++++++++ .../flywheel/core/layout/CommonItems.java | 24 +++++ .../layout/LayoutItem.java} | 4 +- .../layout/MatrixItems.java} | 6 +- .../flywheel/core/layout/PaddingItem.java | 19 ++++ .../layout/PrimitiveItem.java} | 8 +- .../flywheel/core/layout/package-info.java | 6 ++ ...asicWriter.java => BasicWriterUnsafe.java} | 4 +- .../core/materials/model/ModelType.java | 16 ++-- ...odelWriter.java => ModelWriterUnsafe.java} | 6 +- .../core/materials/oriented/OrientedType.java | 14 +-- ...dWriter.java => OrientedWriterUnsafe.java} | 6 +- .../flywheel/core/model/BlockModel.java | 6 +- .../jozufozu/flywheel/core/model/Model.java | 20 ++-- .../flywheel/core/model/ModelTransformer.java | 2 +- .../flywheel/core/model/ModelUtil.java | 2 +- .../flywheel/core/model/WorldModel.java | 7 +- .../flywheel/core/vertex/BlockVertex.java | 81 ++++++----------- .../flywheel/core/vertex/BlockVertexList.java | 1 + .../core/vertex/BlockVertexListUnsafe.java | 18 ++-- .../core/vertex/BlockWriterUnsafe.java | 57 ++++++++++++ .../core/vertex/PosTexNormalVertex.java | 34 +++---- .../vertex/PosTexNormalVertexListUnsafe.java | 1 + .../core/vertex/PosTexNormalWriter.java | 42 --------- .../core/vertex/PosTexNormalWriterUnsafe.java | 45 +++++++++ .../flywheel/core/vertex/VertexType.java | 16 ---- .../core/vertex/VertexWriterUnsafe.java | 41 +++++++++ .../flywheel/core/vertex/package-info.java | 6 ++ 50 files changed, 599 insertions(+), 433 deletions(-) rename src/main/java/com/jozufozu/flywheel/{core => api}/vertex/VertexList.java (58%) create mode 100644 src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java create mode 100644 src/main/java/com/jozufozu/flywheel/api/vertex/VertexWriter.java rename src/main/java/com/jozufozu/flywheel/{backend/gl/attrib => api/vertex}/package-info.java (77%) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/attrib/CommonAttributes.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexFormat.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/hardcoded/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java rename src/main/java/com/jozufozu/flywheel/{backend/gl/attrib/IAttribSpec.java => core/layout/LayoutItem.java} (57%) rename src/main/java/com/jozufozu/flywheel/{backend/gl/attrib/MatrixAttributes.java => core/layout/MatrixItems.java} (82%) create mode 100644 src/main/java/com/jozufozu/flywheel/core/layout/PaddingItem.java rename src/main/java/com/jozufozu/flywheel/{backend/gl/attrib/VertexAttribSpec.java => core/layout/PrimitiveItem.java} (77%) create mode 100644 src/main/java/com/jozufozu/flywheel/core/layout/package-info.java rename src/main/java/com/jozufozu/flywheel/core/materials/{UnsafeBasicWriter.java => BasicWriterUnsafe.java} (84%) rename src/main/java/com/jozufozu/flywheel/core/materials/model/{UnsafeModelWriter.java => ModelWriterUnsafe.java} (73%) rename src/main/java/com/jozufozu/flywheel/core/materials/oriented/{UnsafeOrientedWriter.java => OrientedWriterUnsafe.java} (81%) create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/BlockWriterUnsafe.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriterUnsafe.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/VertexWriterUnsafe.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/package-info.java diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java index 5d5e2985e..a405d518c 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java @@ -1,6 +1,6 @@ package com.jozufozu.flywheel.api.struct; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.core.layout.BufferLayout; /** * A StructType contains metadata for a specific instance struct that Flywheel can interface with. @@ -9,13 +9,13 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; public interface StructType { /** - * @return A new, zeroed instance of the struct. + * @return A new, zeroed instance of S. */ S create(); /** - * @return The format descriptor of the struct type. + * @return The layout of S when buffered. */ - VertexFormat format(); + BufferLayout getLayout(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java similarity index 58% rename from src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java rename to src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java index a87df6315..939f42731 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java @@ -1,5 +1,14 @@ -package com.jozufozu.flywheel.core.vertex; +package com.jozufozu.flywheel.api.vertex; +/** + * A read only view of a vertex buffer. + * + *

+ * VertexList assumes nothing about the layout of the vertices. Implementations should feel free to return constants + * for values that are unused in their layout. + *

+ * TODO: more flexible elements? + */ public interface VertexList { float getX(int index); diff --git a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java new file mode 100644 index 000000000..f785f4ac3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java @@ -0,0 +1,42 @@ +package com.jozufozu.flywheel.api.vertex; + +import java.nio.ByteBuffer; + +import com.jozufozu.flywheel.core.layout.BufferLayout; + +/** + * A vertex type containing metadata about a specific vertex layout. + */ +public interface VertexType { + + /** + * The layout of this type of vertex when buffered. + */ + BufferLayout getLayout(); + + /** + * Create a writer backed by the given ByteBuffer. + * + *

+ * Implementors are encouraged to override the return type for ergonomics. + *

+ */ + VertexWriter createWriter(ByteBuffer buffer); + + /** + * Create a view of the given ByteBuffer as if it were already filled with vertices. + * + *

+ * Implementors are encouraged to override the return type for ergonomics. + *

+ */ + VertexList createReader(ByteBuffer buffer, int vertexCount); + + default int getStride() { + return getLayout().getStride(); + } + + default int byteOffset(int vertexIndex) { + return getStride() * vertexIndex; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexWriter.java b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexWriter.java new file mode 100644 index 000000000..ea883815b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexWriter.java @@ -0,0 +1,15 @@ +package com.jozufozu.flywheel.api.vertex; + +public interface VertexWriter { + void writeVertex(VertexList list, int index); + + void seekToVertex(int vertex); + + VertexList intoReader(); + + default void writeVertexList(VertexList list) { + for (int i = 0; i < list.getVertexCount(); i++) { + this.writeVertex(list, i); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/package-info.java b/src/main/java/com/jozufozu/flywheel/api/vertex/package-info.java similarity index 77% rename from src/main/java/com/jozufozu/flywheel/backend/gl/attrib/package-info.java rename to src/main/java/com/jozufozu/flywheel/api/vertex/package-info.java index 5fb341756..3da8d0b05 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/package-info.java @@ -1,5 +1,5 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.backend.gl.attrib; +package com.jozufozu.flywheel.api.vertex; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index ba02ae599..5b65ce868 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -102,7 +102,7 @@ public class Backend { } materialRegistry.put(name, spec); - log.debug("registered material '" + name + "' with instance size " + spec.format().getStride()); + log.debug("registered material '" + name + "' with instance size " + spec.getLayout().getStride()); return spec; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/CommonAttributes.java b/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/CommonAttributes.java deleted file mode 100644 index a1726fae0..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/CommonAttributes.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.jozufozu.flywheel.backend.gl.attrib; - -import com.jozufozu.flywheel.backend.gl.GlNumericType; - -public class CommonAttributes { - - public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlNumericType.FLOAT, 4); - public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlNumericType.FLOAT, 3); - public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlNumericType.FLOAT, 2); - public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlNumericType.FLOAT, 1); - - public static final VertexAttribSpec QUATERNION = new VertexAttribSpec(GlNumericType.FLOAT, 4); - public static final VertexAttribSpec NORMAL = new VertexAttribSpec(GlNumericType.BYTE, 3, true); - public static final VertexAttribSpec UV = new VertexAttribSpec(GlNumericType.FLOAT, 2); - - public static final VertexAttribSpec RGBA = new VertexAttribSpec(GlNumericType.UBYTE, 4, true); - public static final VertexAttribSpec RGB = new VertexAttribSpec(GlNumericType.UBYTE, 3, true); - public static final VertexAttribSpec LIGHT = new VertexAttribSpec(GlNumericType.UBYTE, 2, true); - - public static final VertexAttribSpec NORMALIZED_BYTE = new VertexAttribSpec(GlNumericType.BYTE, 1, true); -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexFormat.java b/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexFormat.java deleted file mode 100644 index 9d48fd142..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexFormat.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jozufozu.flywheel.backend.gl.attrib; - -import java.util.ArrayList; -import java.util.Collections; - -public class VertexFormat { - - private final ArrayList allAttributes; - - private final int numAttributes; - private final int stride; - - public VertexFormat(ArrayList allAttributes) { - this.allAttributes = allAttributes; - - int numAttributes = 0, stride = 0; - for (IAttribSpec spec : allAttributes) { - numAttributes += spec.getAttributeCount(); - stride += spec.getSize(); - } - this.numAttributes = numAttributes; - this.stride = stride; - } - - public int getAttributeCount() { - return numAttributes; - } - - public int getStride() { - return stride; - } - - public void vertexAttribPointers(int index) { - int offset = 0; - for (IAttribSpec spec : this.allAttributes) { - spec.vertexAttribPointer(stride, index, offset); - index += spec.getAttributeCount(); - offset += spec.getSize(); - } - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private final ArrayList allAttributes = new ArrayList<>(); - - public Builder() { - } - - public Builder addAttributes(IAttribSpec... attributes) { - Collections.addAll(allAttributes, attributes); - return this; - } - - public VertexFormat build() { - return new VertexFormat(allAttributes); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index a13df5f17..3a8731f71 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -11,14 +11,14 @@ import com.jozufozu.flywheel.core.model.Model; public abstract class AbstractInstancer implements Instancer { - protected final Supplier type; + protected final Supplier factory; protected final Model modelData; protected final ArrayList data = new ArrayList<>(); protected boolean anyToRemove; - protected AbstractInstancer(Supplier type, Model modelData) { - this.type = type; + protected AbstractInstancer(Supplier factory, Model modelData) { + this.factory = factory; this.modelData = modelData; } @@ -27,7 +27,7 @@ public abstract class AbstractInstancer implements Insta */ @Override public D createInstance() { - return _add(type.get()); + return _add(factory.get()); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index a753b0b05..ed92586cf 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -1,21 +1,18 @@ package com.jozufozu.flywheel.backend.instancing.instancing; -import java.util.BitSet; - import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.Instanced; -import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlVertexArray; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.error.GlError; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; -import com.jozufozu.flywheel.backend.model.IBufferedModel; +import com.jozufozu.flywheel.backend.model.BufferedModel; import com.jozufozu.flywheel.backend.model.ModelAllocator; import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.util.AttribUtil; @@ -23,10 +20,10 @@ import com.jozufozu.flywheel.util.AttribUtil; public class GPUInstancer extends AbstractInstancer { private final ModelAllocator modelAllocator; - private final VertexFormat instanceFormat; + private final BufferLayout instanceFormat; private final Instanced instancedType; - private IBufferedModel model; + private BufferedModel model; private GlVertexArray vao; private GlBuffer instanceVBO; private int glBufferSize = -1; @@ -39,7 +36,7 @@ public class GPUInstancer extends AbstractInstancer { public GPUInstancer(Instanced type, Model model, ModelAllocator modelAllocator) { super(type::create, model); this.modelAllocator = modelAllocator; - this.instanceFormat = type.format(); + this.instanceFormat = type.getLayout(); instancedType = type; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java index e73844a0f..2c7bf5cb9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java @@ -1,90 +1,41 @@ package com.jozufozu.flywheel.backend.model; -import static org.lwjgl.opengl.GL11.glDrawArrays; +import com.jozufozu.flywheel.core.layout.BufferLayout; +import com.jozufozu.flywheel.api.vertex.VertexType; -import org.lwjgl.opengl.GL31; +public interface BufferedModel { -import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.backend.gl.GlPrimitive; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; -import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; -import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; -import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; -import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.util.AttribUtil; + VertexType getType(); -public class BufferedModel implements IBufferedModel { - - protected final Model model; - protected final GlPrimitive primitiveMode; - protected GlBuffer vbo; - protected boolean deleted; - - public BufferedModel(GlPrimitive primitiveMode, Model model) { - this.model = model; - this.primitiveMode = primitiveMode; - - vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); - - vbo.bind(); - // allocate the buffer on the gpu - vbo.alloc(model.size()); - - // mirror it in system memory so we can write to it, and upload our model. - try (MappedBuffer buffer = vbo.getBuffer(0, model.size())) { - model.getType().copyInto(buffer.unwrap(), model.getReader()); - } catch (Exception e) { - Flywheel.log.error(String.format("Error uploading model '%s':", model.name()), e); - } - - vbo.unbind(); - } - - public boolean isDeleted() { - return deleted; - } - - public VertexFormat getFormat() { - return model.format(); - } - - public int getVertexCount() { - return model.vertexCount(); - } + int getVertexCount(); /** * The VBO/VAO should be bound externally. */ - public void setupState() { - vbo.bind(); - AttribUtil.enableArrays(getAttributeCount()); - getFormat().vertexAttribPointers(0); - } + void setupState(); - public void clearState() { - AttribUtil.disableArrays(getAttributeCount()); - vbo.unbind(); - } + void clearState(); - public void drawCall() { - glDrawArrays(primitiveMode.glEnum, 0, getVertexCount()); - } + void drawCall(); /** * Draws many instances of this model, assuming the appropriate state is already bound. */ - public void drawInstances(int instanceCount) { - if (!valid()) return; + void drawInstances(int instanceCount); - GL31.glDrawArraysInstanced(primitiveMode.glEnum, 0, getVertexCount(), instanceCount); + boolean isDeleted(); + + void delete(); + + default BufferLayout getFormat() { + return getType().getLayout(); } - public void delete() { - if (deleted) return; + default boolean valid() { + return getVertexCount() > 0 && !isDeleted(); + } - deleted = true; - vbo.delete(); + default int getAttributeCount() { + return getFormat().getAttributeCount(); } } - diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java deleted file mode 100644 index 6db741c06..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.jozufozu.flywheel.backend.model; - -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; - -public interface IBufferedModel { - - VertexFormat getFormat(); - - int getVertexCount(); - - /** - * The VBO/VAO should be bound externally. - */ - void setupState(); - - void clearState(); - - void drawCall(); - - /** - * Draws many instances of this model, assuming the appropriate state is already bound. - */ - void drawInstances(int instanceCount); - - boolean isDeleted(); - - void delete(); - - default boolean valid() { - return getVertexCount() > 0 && !isDeleted(); - } - - default int getAttributeCount() { - return getFormat().getAttributeCount(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ImmediateAllocator.java b/src/main/java/com/jozufozu/flywheel/backend/model/ImmediateAllocator.java index e290549e4..30240d8d8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ImmediateAllocator.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ImmediateAllocator.java @@ -7,7 +7,7 @@ public class ImmediateAllocator implements ModelAllocator { public static final ImmediateAllocator INSTANCE = new ImmediateAllocator(); @Override - public IBufferedModel alloc(Model model, Callback allocationCallback) { + public BufferedModel alloc(Model model, Callback allocationCallback) { IndexedModel out = new IndexedModel(model); allocationCallback.onAlloc(out); return out; diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java index 02ab9606b..06bd043b2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java @@ -11,7 +11,7 @@ import com.jozufozu.flywheel.core.model.Model; * *
This should be favored over a normal BufferedModel. */ -public class IndexedModel extends BufferedModel { +public class IndexedModel extends VBOModel { protected ElementBuffer ebo; diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelAllocator.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelAllocator.java index 8756f9427..2b5424ffe 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelAllocator.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelAllocator.java @@ -9,10 +9,10 @@ public interface ModelAllocator { * @param model The model to allocate. * @return A handle to the allocated model. */ - IBufferedModel alloc(Model model, Callback allocationCallback); + BufferedModel alloc(Model model, Callback allocationCallback); @FunctionalInterface interface Callback { - void onAlloc(IBufferedModel arenaModel); + void onAlloc(BufferedModel arenaModel); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java index 9d26f2abb..c93fe945c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.backend.model; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -8,13 +7,13 @@ import org.lwjgl.opengl.GL32; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.gl.GlPrimitive; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.core.vertex.VertexType; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.api.vertex.VertexWriter; import com.jozufozu.flywheel.util.AttribUtil; public class ModelPool implements ModelAllocator { @@ -118,13 +117,13 @@ public class ModelPool implements ModelAllocator { private void uploadAll() { try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { - ByteBuffer buf = buffer.unwrap(); + VertexWriter writer = vertexType.createWriter(buffer.unwrap()); int vertices = 0; for (PooledModel model : models) { model.first = vertices; - buffer(buf, model); + buffer(writer, model); vertices += model.getVertexCount(); } @@ -136,9 +135,9 @@ public class ModelPool implements ModelAllocator { private void uploadPending() { try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { - ByteBuffer buf = buffer.unwrap(); + VertexWriter writer = vertexType.createWriter(buffer.unwrap()); for (PooledModel model : pendingUpload) { - buffer(buf, model); + buffer(writer, model); } pendingUpload.clear(); } catch (Exception e) { @@ -146,10 +145,9 @@ public class ModelPool implements ModelAllocator { } } - private void buffer(ByteBuffer buf, PooledModel model) { - int pos = model.first * vertexType.getStride(); - buf.position(pos); - vertexType.copyInto(buf, model.model.getReader()); + private void buffer(VertexWriter writer, PooledModel model) { + writer.seekToVertex(model.first); + writer.writeVertexList(model.model.getReader()); if (model.callback != null) model.callback.onAlloc(model); } @@ -161,7 +159,7 @@ public class ModelPool implements ModelAllocator { vbo.delete(); } - public class PooledModel implements IBufferedModel { + public class PooledModel implements BufferedModel { private final ElementBuffer ebo; private Callback callback; @@ -178,8 +176,8 @@ public class ModelPool implements ModelAllocator { } @Override - public VertexFormat getFormat() { - return model.format(); + public VertexType getType() { + return ModelPool.this.vertexType; } @Override @@ -192,7 +190,7 @@ public class ModelPool implements ModelAllocator { vbo.bind(); ebo.bind(); AttribUtil.enableArrays(getAttributeCount()); - getFormat().vertexAttribPointers(0); + ModelPool.this.vertexType.getLayout().vertexAttribPointers(0); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java index b6802caad..2debba1ab 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java @@ -7,7 +7,7 @@ import com.jozufozu.flywheel.core.model.Model; public class ModelRenderer { protected Supplier modelSupplier; - protected IBufferedModel model; + protected BufferedModel model; protected boolean initialized; diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java new file mode 100644 index 000000000..fee075c2a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java @@ -0,0 +1,91 @@ +package com.jozufozu.flywheel.backend.model; + +import static org.lwjgl.opengl.GL11.glDrawArrays; + +import org.lwjgl.opengl.GL31; + +import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.backend.gl.GlPrimitive; +import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; +import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; +import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; +import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; +import com.jozufozu.flywheel.core.model.Model; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.util.AttribUtil; + +public class VBOModel implements BufferedModel { + + protected final Model model; + protected final GlPrimitive primitiveMode; + protected GlBuffer vbo; + protected boolean deleted; + + public VBOModel(GlPrimitive primitiveMode, Model model) { + this.model = model; + this.primitiveMode = primitiveMode; + + vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); + + vbo.bind(); + // allocate the buffer on the gpu + vbo.alloc(model.size()); + + // mirror it in system memory so we can write to it, and upload our model. + try (MappedBuffer buffer = vbo.getBuffer(0, model.size())) { + model.writeInto(buffer.unwrap()); + } catch (Exception e) { + Flywheel.log.error(String.format("Error uploading model '%s':", model.name()), e); + } + + vbo.unbind(); + } + + public boolean isDeleted() { + return deleted; + } + + @Override + public VertexType getType() { + return model.getType(); + } + + public int getVertexCount() { + return model.vertexCount(); + } + + /** + * The VBO/VAO should be bound externally. + */ + public void setupState() { + vbo.bind(); + AttribUtil.enableArrays(getAttributeCount()); + getFormat().vertexAttribPointers(0); + } + + public void clearState() { + AttribUtil.disableArrays(getAttributeCount()); + vbo.unbind(); + } + + public void drawCall() { + glDrawArrays(primitiveMode.glEnum, 0, getVertexCount()); + } + + /** + * Draws many instances of this model, assuming the appropriate state is already bound. + */ + public void drawInstances(int instanceCount) { + if (!valid()) return; + + GL31.glDrawArraysInstanced(primitiveMode.glEnum, 0, getVertexCount(), instanceCount); + } + + public void delete() { + if (deleted) return; + + deleted = true; + vbo.delete(); + } +} + diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java index 94f976f22..0033496b3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java @@ -2,20 +2,17 @@ package com.jozufozu.flywheel.backend.struct; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructWriter; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; public abstract class BufferWriter implements StructWriter { protected final VecBuffer backingBuffer; - protected final VertexFormat format; protected final int stride; protected BufferWriter(VecBuffer backingBuffer, StructType vertexType) { this.backingBuffer = backingBuffer; - this.format = vertexType.format(); - this.stride = this.format.getStride(); + this.stride = vertexType.getLayout().getStride(); } @Override @@ -36,6 +33,6 @@ public abstract class BufferWriter implements StructWriter { @Override public void seek(int pos) { - backingBuffer.position(pos * stride); + backingBuffer.position(pos * stride); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java index 6dadbd2f3..77078ffff 100644 --- a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java @@ -2,17 +2,17 @@ package com.jozufozu.flywheel.core.hardcoded; import java.util.List; +import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.core.vertex.VertexList; -import com.jozufozu.flywheel.core.vertex.PosTexNormalVertexListUnsafe; -import com.jozufozu.flywheel.core.vertex.PosTexNormalWriter; +import com.jozufozu.flywheel.api.vertex.VertexList; +import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe; import com.mojang.blaze3d.platform.MemoryTracker; public class ModelPart implements Model { private final int vertices; private final String name; - private final PosTexNormalVertexListUnsafe reader; + private final VertexList reader; public ModelPart(List cuboids, String name) { this.name = name; @@ -25,7 +25,7 @@ public class ModelPart implements Model { this.vertices = vertices; } - PosTexNormalWriter writer = new PosTexNormalWriter(MemoryTracker.create(size())); + PosTexNormalWriterUnsafe writer = Formats.POS_TEX_NORMAL.createWriter(MemoryTracker.create(size())); for (PartBuilder.CuboidBuilder cuboid : cuboids) { cuboid.buffer(writer); } diff --git a/src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java index b5d7a503a..3747fbe62 100644 --- a/src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java @@ -5,7 +5,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Set; -import com.jozufozu.flywheel.core.vertex.PosTexNormalWriter; +import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe; import com.mojang.math.Matrix3f; import com.mojang.math.Quaternion; import com.mojang.math.Vector3f; @@ -160,7 +160,7 @@ public class PartBuilder { return visibleFaces.size() * 4; } - public void buffer(PosTexNormalWriter buffer) { + public void buffer(PosTexNormalWriterUnsafe buffer) { float sizeX = posX2 - posX1; float sizeY = posY2 - posY1; @@ -235,7 +235,7 @@ public class PartBuilder { } } - public void quad(PosTexNormalWriter buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) { + public void quad(PosTexNormalWriterUnsafe buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) { buffer.putVertex(vertices[0].x(), vertices[0].y(), vertices[0].z(), normal.x(), normal.y(), normal.z(), maxU, minV); buffer.putVertex(vertices[1].x(), vertices[1].y(), vertices[1].z(), normal.x(), normal.y(), normal.z(), minU, minV); buffer.putVertex(vertices[2].x(), vertices[2].y(), vertices[2].z(), normal.x(), normal.y(), normal.z(), minU, maxV); diff --git a/src/main/java/com/jozufozu/flywheel/core/hardcoded/package-info.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/package-info.java new file mode 100644 index 000000000..f90a44c62 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.core.hardcoded; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java new file mode 100644 index 000000000..cbfb4fcc7 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java @@ -0,0 +1,74 @@ +package com.jozufozu.flywheel.core.layout; + +import java.util.List; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.api.vertex.VertexType; + +/** + * Classic Vertex Format struct with a clever name. + * + *

+ * Used for vertices and instance. Describes the layout of a datatype in a buffer object. + *

+ * + * @see com.jozufozu.flywheel.api.struct.StructType + * @see VertexType + */ +public class BufferLayout { + + private final List allAttributes; + + private final int numAttributes; + private final int stride; + + public BufferLayout(List allAttributes) { + this.allAttributes = allAttributes; + + int numAttributes = 0, stride = 0; + for (LayoutItem spec : allAttributes) { + numAttributes += spec.getAttributeCount(); + stride += spec.getSize(); + } + this.numAttributes = numAttributes; + this.stride = stride; + } + + public int getAttributeCount() { + return numAttributes; + } + + public int getStride() { + return stride; + } + + public void vertexAttribPointers(int index) { + int offset = 0; + for (LayoutItem spec : this.allAttributes) { + spec.vertexAttribPointer(stride, index, offset); + index += spec.getAttributeCount(); + offset += spec.getSize(); + } + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private final ImmutableList.Builder allItems; + + public Builder() { + allItems = ImmutableList.builder(); + } + + public Builder addItems(LayoutItem... attributes) { + allItems.add(attributes); + return this; + } + + public BufferLayout build() { + return new BufferLayout(allItems.build()); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java b/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java new file mode 100644 index 000000000..1021296ac --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java @@ -0,0 +1,24 @@ +package com.jozufozu.flywheel.core.layout; + +import com.jozufozu.flywheel.backend.gl.GlNumericType; + +public class CommonItems { + + public static final PrimitiveItem VEC4 = new PrimitiveItem(GlNumericType.FLOAT, 4); + public static final PrimitiveItem VEC3 = new PrimitiveItem(GlNumericType.FLOAT, 3); + public static final PrimitiveItem VEC2 = new PrimitiveItem(GlNumericType.FLOAT, 2); + public static final PrimitiveItem FLOAT = new PrimitiveItem(GlNumericType.FLOAT, 1); + + public static final PrimitiveItem QUATERNION = new PrimitiveItem(GlNumericType.FLOAT, 4); + public static final PrimitiveItem NORMAL = new PrimitiveItem(GlNumericType.BYTE, 3, true); + public static final PrimitiveItem UV = new PrimitiveItem(GlNumericType.FLOAT, 2); + + public static final PrimitiveItem RGBA = new PrimitiveItem(GlNumericType.UBYTE, 4, true); + public static final PrimitiveItem RGB = new PrimitiveItem(GlNumericType.UBYTE, 3, true); + public static final PrimitiveItem LIGHT = new PrimitiveItem(GlNumericType.UBYTE, 2, true); + public static final PrimitiveItem LIGHT_SHORT = new PrimitiveItem(GlNumericType.USHORT, 2, true); + + public static final PrimitiveItem NORMALIZED_BYTE = new PrimitiveItem(GlNumericType.BYTE, 1, true); + public static final LayoutItem PADDING_BYTE = new PaddingItem(1); + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/IAttribSpec.java b/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java similarity index 57% rename from src/main/java/com/jozufozu/flywheel/backend/gl/attrib/IAttribSpec.java rename to src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java index 04a8daf00..ae910dc51 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/IAttribSpec.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.backend.gl.attrib; +package com.jozufozu.flywheel.core.layout; -public interface IAttribSpec { +public interface LayoutItem { void vertexAttribPointer(int stride, int index, int offset); diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/MatrixAttributes.java b/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItems.java similarity index 82% rename from src/main/java/com/jozufozu/flywheel/backend/gl/attrib/MatrixAttributes.java rename to src/main/java/com/jozufozu/flywheel/core/layout/MatrixItems.java index aef66036c..f9d8ffe2e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/MatrixAttributes.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItems.java @@ -1,10 +1,10 @@ -package com.jozufozu.flywheel.backend.gl.attrib; +package com.jozufozu.flywheel.core.layout; import org.lwjgl.opengl.GL20; import com.jozufozu.flywheel.backend.gl.GlNumericType; -public enum MatrixAttributes implements IAttribSpec { +public enum MatrixItems implements LayoutItem { MAT3(3, 3), MAT4(4, 4), ; @@ -12,7 +12,7 @@ public enum MatrixAttributes implements IAttribSpec { private final int rows; private final int cols; - MatrixAttributes(int rows, int cols) { + MatrixItems(int rows, int cols) { this.rows = rows; this.cols = cols; } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/PaddingItem.java b/src/main/java/com/jozufozu/flywheel/core/layout/PaddingItem.java new file mode 100644 index 000000000..788aa9b3d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/layout/PaddingItem.java @@ -0,0 +1,19 @@ +package com.jozufozu.flywheel.core.layout; + +record PaddingItem(int bytes) implements LayoutItem { + + @Override + public void vertexAttribPointer(int stride, int index, int offset) { + + } + + @Override + public int getSize() { + return bytes; + } + + @Override + public int getAttributeCount() { + return 0; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexAttribSpec.java b/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java similarity index 77% rename from src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexAttribSpec.java rename to src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java index 7219ddb20..be1373867 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexAttribSpec.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java @@ -1,10 +1,10 @@ -package com.jozufozu.flywheel.backend.gl.attrib; +package com.jozufozu.flywheel.core.layout; import org.lwjgl.opengl.GL20; import com.jozufozu.flywheel.backend.gl.GlNumericType; -public class VertexAttribSpec implements IAttribSpec { +public class PrimitiveItem implements LayoutItem { private final GlNumericType type; private final int count; @@ -12,11 +12,11 @@ public class VertexAttribSpec implements IAttribSpec { private final int attributeCount; private final boolean normalized; - public VertexAttribSpec(GlNumericType type, int count) { + public PrimitiveItem(GlNumericType type, int count) { this(type, count, false); } - public VertexAttribSpec(GlNumericType type, int count, boolean normalized) { + public PrimitiveItem(GlNumericType type, int count, boolean normalized) { this.type = type; this.count = count; this.size = type.getByteWidth() * count; diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/package-info.java b/src/main/java/com/jozufozu/flywheel/core/layout/package-info.java new file mode 100644 index 000000000..3f91c99ea --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/layout/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.core.layout; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/UnsafeBasicWriter.java b/src/main/java/com/jozufozu/flywheel/core/materials/BasicWriterUnsafe.java similarity index 84% rename from src/main/java/com/jozufozu/flywheel/core/materials/UnsafeBasicWriter.java rename to src/main/java/com/jozufozu/flywheel/core/materials/BasicWriterUnsafe.java index 5b36599e0..1ece742b2 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/UnsafeBasicWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/BasicWriterUnsafe.java @@ -6,9 +6,9 @@ import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter; -public abstract class UnsafeBasicWriter extends UnsafeBufferWriter { +public abstract class BasicWriterUnsafe extends UnsafeBufferWriter { - public UnsafeBasicWriter(VecBuffer backingBuffer, StructType vertexType) { + public BasicWriterUnsafe(VecBuffer backingBuffer, StructType vertexType) { super(backingBuffer, vertexType); } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java index b2f7090fd..fdd5127b0 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java @@ -3,9 +3,9 @@ package com.jozufozu.flywheel.core.materials.model; import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructWriter; -import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; -import com.jozufozu.flywheel.backend.gl.attrib.MatrixAttributes; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.core.layout.CommonItems; +import com.jozufozu.flywheel.core.layout.MatrixItems; +import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.Programs; import com.jozufozu.flywheel.core.model.ModelTransformer; @@ -14,9 +14,9 @@ import net.minecraft.resources.ResourceLocation; public class ModelType implements Instanced, Batched { - public static final VertexFormat FORMAT = VertexFormat.builder() - .addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA) - .addAttributes(MatrixAttributes.MAT4, MatrixAttributes.MAT3) + public static final BufferLayout FORMAT = BufferLayout.builder() + .addItems(CommonItems.LIGHT, CommonItems.RGBA) + .addItems(MatrixItems.MAT4, MatrixItems.MAT3) .build(); @Override @@ -25,13 +25,13 @@ public class ModelType implements Instanced, Batched { } @Override - public VertexFormat format() { + public BufferLayout getLayout() { return FORMAT; } @Override public StructWriter getWriter(VecBuffer backing) { - return new UnsafeModelWriter(backing, this); + return new ModelWriterUnsafe(backing, this); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/UnsafeModelWriter.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java similarity index 73% rename from src/main/java/com/jozufozu/flywheel/core/materials/model/UnsafeModelWriter.java rename to src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java index 9d041df58..ba842aabd 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/UnsafeModelWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java @@ -2,12 +2,12 @@ package com.jozufozu.flywheel.core.materials.model; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.core.materials.UnsafeBasicWriter; +import com.jozufozu.flywheel.core.materials.BasicWriterUnsafe; import com.jozufozu.flywheel.util.WriteUnsafe; -public class UnsafeModelWriter extends UnsafeBasicWriter { +public class ModelWriterUnsafe extends BasicWriterUnsafe { - public UnsafeModelWriter(VecBuffer backingBuffer, StructType vertexType) { + public ModelWriterUnsafe(VecBuffer backingBuffer, StructType vertexType) { super(backingBuffer, vertexType); } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java index 4bd6d9c52..d2f7e57eb 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java @@ -3,8 +3,8 @@ package com.jozufozu.flywheel.core.materials.oriented; import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructWriter; -import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.core.layout.CommonItems; +import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.Programs; import com.jozufozu.flywheel.core.model.ModelTransformer; @@ -14,9 +14,9 @@ import net.minecraft.resources.ResourceLocation; public class OrientedType implements Instanced, Batched { - public static final VertexFormat FORMAT = VertexFormat.builder() - .addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA) - .addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION) + public static final BufferLayout FORMAT = BufferLayout.builder() + .addItems(CommonItems.LIGHT, CommonItems.RGBA) + .addItems(CommonItems.VEC3, CommonItems.VEC3, CommonItems.QUATERNION) .build(); @Override @@ -25,13 +25,13 @@ public class OrientedType implements Instanced, Batched getWriter(VecBuffer backing) { - return new UnsafeOrientedWriter(backing, this); + return new OrientedWriterUnsafe(backing, this); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/UnsafeOrientedWriter.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedWriterUnsafe.java similarity index 81% rename from src/main/java/com/jozufozu/flywheel/core/materials/oriented/UnsafeOrientedWriter.java rename to src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedWriterUnsafe.java index c617e6396..201faf6eb 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/UnsafeOrientedWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedWriterUnsafe.java @@ -4,10 +4,10 @@ import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.core.materials.UnsafeBasicWriter; +import com.jozufozu.flywheel.core.materials.BasicWriterUnsafe; -public class UnsafeOrientedWriter extends UnsafeBasicWriter { - public UnsafeOrientedWriter(VecBuffer backingBuffer, StructType vertexType) { +public class OrientedWriterUnsafe extends BasicWriterUnsafe { + public OrientedWriterUnsafe(VecBuffer backingBuffer, StructType vertexType) { super(backingBuffer, vertexType); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java index 93a52f897..bfb01f00b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java @@ -1,7 +1,7 @@ package com.jozufozu.flywheel.core.model; -import com.jozufozu.flywheel.core.vertex.VertexList; -import com.jozufozu.flywheel.core.vertex.BlockVertexListUnsafe; +import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.api.vertex.VertexList; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; @@ -29,7 +29,7 @@ public class BlockModel implements Model { } public BlockModel(BakedModel model, BlockState referenceState, PoseStack ms) { - reader = new BlockVertexListUnsafe(ModelUtil.getBufferBuilder(model, referenceState, ms)); + reader = Formats.BLOCK.createReader(ModelUtil.getBufferBuilder(model, referenceState, ms)); name = referenceState.toString(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Model.java b/src/main/java/com/jozufozu/flywheel/core/model/Model.java index 6ad820da1..e505ba74b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/Model.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/Model.java @@ -1,11 +1,12 @@ package com.jozufozu.flywheel.core.model; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import java.nio.ByteBuffer; + import com.jozufozu.flywheel.backend.model.ElementBuffer; import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.QuadConverter; -import com.jozufozu.flywheel.core.vertex.VertexList; -import com.jozufozu.flywheel.core.vertex.VertexType; +import com.jozufozu.flywheel.api.vertex.VertexList; +import com.jozufozu.flywheel.api.vertex.VertexType; /** * A model that can be rendered by flywheel. @@ -49,13 +50,6 @@ public interface Model { return Formats.POS_TEX_NORMAL; } - /** - * @return The format of this model's vertices - */ - default VertexFormat format() { - return getType().getFormat(); - } - /** * Create an element buffer object that indexes the vertices of this model. * @@ -75,7 +69,7 @@ public interface Model { * The size in bytes that this model's data takes up. */ default int size() { - return vertexCount() * format().getStride(); + return getType().byteOffset(vertexCount()); } /** @@ -85,4 +79,8 @@ public interface Model { default boolean empty() { return vertexCount() == 0; } + + default void writeInto(ByteBuffer buffer) { + getType().createWriter(buffer).writeVertexList(getReader()); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java index 336dccae5..fc2223da5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java @@ -1,6 +1,6 @@ package com.jozufozu.flywheel.core.model; -import com.jozufozu.flywheel.core.vertex.VertexList; +import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.util.RenderMath; import com.jozufozu.flywheel.util.transform.Transform; import com.mojang.blaze3d.vertex.PoseStack; diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java index 365751f62..df17f63c4 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -55,7 +55,7 @@ public class ModelUtil { builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); blockRenderer.tesselateBlock(VirtualEmptyBlockGetter.INSTANCE, model, referenceState, BlockPos.ZERO, ms, builder, - true, new Random(), 42, OverlayTexture.NO_OVERLAY, VirtualEmptyModelData.INSTANCE); + false, new Random(), 42, OverlayTexture.NO_OVERLAY, VirtualEmptyModelData.INSTANCE); builder.end(); return builder; } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java index e390bdbc5..b910de2a6 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java @@ -3,9 +3,8 @@ package com.jozufozu.flywheel.core.model; import java.util.Collection; import com.jozufozu.flywheel.core.Formats; -import com.jozufozu.flywheel.core.vertex.VertexList; -import com.jozufozu.flywheel.core.vertex.VertexType; -import com.jozufozu.flywheel.core.vertex.BlockVertexListUnsafe; +import com.jozufozu.flywheel.api.vertex.VertexList; +import com.jozufozu.flywheel.api.vertex.VertexType; import net.minecraft.client.renderer.RenderType; import net.minecraft.world.level.BlockAndTintGetter; @@ -17,7 +16,7 @@ public class WorldModel implements Model { private final String name; public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection blocks, String name) { - reader = new BlockVertexListUnsafe(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); + reader = Formats.BLOCK.createReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); this.name = name; } diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java index 2264443e9..eb77286fc 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java @@ -2,70 +2,49 @@ package com.jozufozu.flywheel.core.vertex; import java.nio.ByteBuffer; -import org.lwjgl.system.MemoryUtil; - -import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.util.RenderMath; - -import net.minecraft.client.renderer.LightTexture; +import com.jozufozu.flywheel.api.vertex.VertexList; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.core.layout.CommonItems; +import com.jozufozu.flywheel.core.layout.BufferLayout; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.datafixers.util.Pair; public class BlockVertex implements VertexType { - public static final VertexFormat FORMAT = VertexFormat.builder() - .addAttributes(CommonAttributes.VEC3, - CommonAttributes.UV, - CommonAttributes.RGBA, - CommonAttributes.LIGHT, - CommonAttributes.NORMAL) + public static final BufferLayout FORMAT = BufferLayout.builder() + .addItems(CommonItems.VEC3, + CommonItems.RGBA, + CommonItems.UV, + CommonItems.LIGHT_SHORT, + CommonItems.NORMAL, + CommonItems.PADDING_BYTE) .build(); @Override - public VertexFormat getFormat() { + public BufferLayout getLayout() { return FORMAT; } @Override - public void copyInto(ByteBuffer buffer, VertexList reader) { - int stride = getStride(); - long addr = MemoryUtil.memAddress(buffer); + public BlockWriterUnsafe createWriter(ByteBuffer buffer) { + return new BlockWriterUnsafe(this, buffer); + } - int vertexCount = reader.getVertexCount(); - for (int i = 0; i < vertexCount; i++) { - float x = reader.getX(i); - float y = reader.getY(i); - float z = reader.getZ(i); + @Override + public BlockVertexListUnsafe createReader(ByteBuffer buffer, int vertexCount) { + return new BlockVertexListUnsafe(buffer, vertexCount); + } - float xN = reader.getNX(i); - float yN = reader.getNY(i); - float zN = reader.getNZ(i); + public VertexList createReader(BufferBuilder bufferBuilder) { + // TODO: try to avoid virtual model rendering + Pair pair = bufferBuilder.popNextBuffer(); + BufferBuilder.DrawState drawState = pair.getFirst(); - float u = reader.getU(i); - float v = reader.getV(i); - - byte r = reader.getR(i); - byte g = reader.getG(i); - byte b = reader.getB(i); - byte a = reader.getA(i); - - int light = reader.getLight(i); - - MemoryUtil.memPutFloat(addr, x); - MemoryUtil.memPutFloat(addr + 4, y); - MemoryUtil.memPutFloat(addr + 8, z); - MemoryUtil.memPutFloat(addr + 12, u); - MemoryUtil.memPutFloat(addr + 16, v); - MemoryUtil.memPutByte(addr + 20, r); - MemoryUtil.memPutByte(addr + 21, g); - MemoryUtil.memPutByte(addr + 22, b); - MemoryUtil.memPutByte(addr + 23, a); - MemoryUtil.memPutByte(addr + 24, (byte) (LightTexture.block(light) << 4)); - MemoryUtil.memPutByte(addr + 25, (byte) (LightTexture.sky(light) << 4)); - MemoryUtil.memPutByte(addr + 26, RenderMath.nb(xN)); - MemoryUtil.memPutByte(addr + 27, RenderMath.nb(yN)); - MemoryUtil.memPutByte(addr + 28, RenderMath.nb(zN)); - - addr += stride; + if (drawState.format() != DefaultVertexFormat.BLOCK) { + throw new RuntimeException("Cannot use BufferBuilder with " + drawState.format()); } + + return new BlockVertexListUnsafe(pair.getSecond(), drawState.vertexCount()); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java index a316d26e6..ff4c98126 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.core.vertex; import java.nio.ByteBuffer; +import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.util.RenderMath; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.datafixers.util.Pair; diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java index d47cf6839..7c93b29d6 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java @@ -4,27 +4,23 @@ import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; +import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.util.RenderMath; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.VertexFormat; -import com.mojang.datafixers.util.Pair; public class BlockVertexListUnsafe implements VertexList { + private final ByteBuffer buffer; private final int vertexCount; - private final int stride; private final long base; - public BlockVertexListUnsafe(BufferBuilder builder) { - VertexFormat vertexFormat = builder.getVertexFormat(); - Pair data = builder.popNextBuffer(); - this.base = MemoryUtil.memAddress(data.getSecond()); - this.vertexCount = data.getFirst().vertexCount(); - this.stride = vertexFormat.getVertexSize(); + public BlockVertexListUnsafe(ByteBuffer buffer, int vertexCount) { + this.buffer = buffer; + this.base = MemoryUtil.memAddress(buffer); + this.vertexCount = vertexCount; } private long ptr(long index) { - return base + index * stride; + return base + index * 32; } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockWriterUnsafe.java new file mode 100644 index 000000000..84d49dee9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockWriterUnsafe.java @@ -0,0 +1,57 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.vertex.VertexList; +import com.jozufozu.flywheel.util.RenderMath; + +public class BlockWriterUnsafe extends VertexWriterUnsafe { + + public BlockWriterUnsafe(BlockVertex type, ByteBuffer buffer) { + super(type, buffer); + } + + @Override + public void writeVertex(VertexList list, int i) { + float x = list.getX(i); + float y = list.getY(i); + float z = list.getZ(i); + + float xN = list.getNX(i); + float yN = list.getNY(i); + float zN = list.getNZ(i); + + float u = list.getU(i); + float v = list.getV(i); + + byte r = list.getR(i); + byte g = list.getG(i); + byte b = list.getB(i); + byte a = list.getA(i); + + int light = list.getLight(i); + + putVertex(x, y, z, u, v, r, g, b, a, light, xN, yN, zN); + } + + public void putVertex(float x, float y, float z, float u, float v, byte r, byte g, byte b, byte a, int light, float nX, float nY, float nZ) { + MemoryUtil.memPutFloat(ptr, x); + MemoryUtil.memPutFloat(ptr + 4, y); + MemoryUtil.memPutFloat(ptr + 8, z); + MemoryUtil.memPutByte(ptr + 12, r); + MemoryUtil.memPutByte(ptr + 13, g); + MemoryUtil.memPutByte(ptr + 14, b); + MemoryUtil.memPutByte(ptr + 15, a); + MemoryUtil.memPutFloat(ptr + 16, u); + MemoryUtil.memPutFloat(ptr + 20, v); + MemoryUtil.memPutInt(ptr + 24, light); + MemoryUtil.memPutByte(ptr + 28, RenderMath.nb(nX)); + MemoryUtil.memPutByte(ptr + 29, RenderMath.nb(nY)); + MemoryUtil.memPutByte(ptr + 30, RenderMath.nb(nZ)); + + ptr += 32; + advance(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java index 590c7d82e..feb7ff348 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java @@ -2,38 +2,28 @@ package com.jozufozu.flywheel.core.vertex; import java.nio.ByteBuffer; -import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.core.layout.CommonItems; +import com.jozufozu.flywheel.core.layout.BufferLayout; public class PosTexNormalVertex implements VertexType { - public static final VertexFormat FORMAT = VertexFormat.builder() - .addAttributes(CommonAttributes.VEC3, CommonAttributes.UV, CommonAttributes.NORMAL) + public static final BufferLayout FORMAT = BufferLayout.builder() + .addItems(CommonItems.VEC3, CommonItems.UV, CommonItems.NORMAL) .build(); @Override - public VertexFormat getFormat() { + public BufferLayout getLayout() { return FORMAT; } @Override - public void copyInto(ByteBuffer buffer, VertexList reader) { - PosTexNormalWriter writer = new PosTexNormalWriter(buffer); + public PosTexNormalWriterUnsafe createWriter(ByteBuffer buffer) { + return new PosTexNormalWriterUnsafe(this, buffer); + } - int vertexCount = reader.getVertexCount(); - for (int i = 0; i < vertexCount; i++) { - float x = reader.getX(i); - float y = reader.getY(i); - float z = reader.getZ(i); - - float u = reader.getU(i); - float v = reader.getV(i); - - float xN = reader.getNX(i); - float yN = reader.getNY(i); - float zN = reader.getNZ(i); - - writer.putVertex(x, y, z, xN, yN, zN, u, v); - } + @Override + public PosTexNormalVertexListUnsafe createReader(ByteBuffer buffer, int vertexCount) { + return new PosTexNormalVertexListUnsafe(buffer, vertexCount); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java index 801c0e56b..70d4a72ee 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java @@ -4,6 +4,7 @@ import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; +import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.util.RenderMath; public class PosTexNormalVertexListUnsafe implements VertexList { diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java deleted file mode 100644 index c04bdd651..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.jozufozu.flywheel.core.vertex; - -import java.nio.ByteBuffer; - -import org.lwjgl.system.MemoryUtil; - -import com.jozufozu.flywheel.util.RenderMath; - -public class PosTexNormalWriter { - - private final ByteBuffer buffer; - private long addr; - - private int vertexCount; - - public PosTexNormalWriter(ByteBuffer buffer) { - this.buffer = buffer; - addr = MemoryUtil.memAddress(buffer); - } - - public void putVertex(float x, float y, float z, float nX, float nY, float nZ, float u, float v) { - MemoryUtil.memPutFloat(addr, x); - MemoryUtil.memPutFloat(addr + 4, y); - MemoryUtil.memPutFloat(addr + 8, z); - MemoryUtil.memPutFloat(addr + 12, u); - MemoryUtil.memPutFloat(addr + 16, v); - MemoryUtil.memPutByte(addr + 20, RenderMath.nb(nX)); - MemoryUtil.memPutByte(addr + 21, RenderMath.nb(nY)); - MemoryUtil.memPutByte(addr + 22, RenderMath.nb(nZ)); - - addr += 23; - vertexCount++; - } - - public int getVertexCount() { - return vertexCount; - } - - public PosTexNormalVertexListUnsafe intoReader() { - return new PosTexNormalVertexListUnsafe(buffer, vertexCount); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriterUnsafe.java new file mode 100644 index 000000000..0eac537f3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriterUnsafe.java @@ -0,0 +1,45 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.vertex.VertexList; +import com.jozufozu.flywheel.util.RenderMath; + +public class PosTexNormalWriterUnsafe extends VertexWriterUnsafe { + + public PosTexNormalWriterUnsafe(PosTexNormalVertex type, ByteBuffer buffer) { + super(type, buffer); + } + + @Override + public void writeVertex(VertexList list, int i) { + float x = list.getX(i); + float y = list.getY(i); + float z = list.getZ(i); + + float u = list.getU(i); + float v = list.getV(i); + + float xN = list.getNX(i); + float yN = list.getNY(i); + float zN = list.getNZ(i); + + putVertex(x, y, z, xN, yN, zN, u, v); + } + + public void putVertex(float x, float y, float z, float nX, float nY, float nZ, float u, float v) { + MemoryUtil.memPutFloat(ptr, x); + MemoryUtil.memPutFloat(ptr + 4, y); + MemoryUtil.memPutFloat(ptr + 8, z); + MemoryUtil.memPutFloat(ptr + 12, u); + MemoryUtil.memPutFloat(ptr + 16, v); + MemoryUtil.memPutByte(ptr + 20, RenderMath.nb(nX)); + MemoryUtil.memPutByte(ptr + 21, RenderMath.nb(nY)); + MemoryUtil.memPutByte(ptr + 22, RenderMath.nb(nZ)); + + ptr += 23; + advance(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java deleted file mode 100644 index d58f1b076..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jozufozu.flywheel.core.vertex; - -import java.nio.ByteBuffer; - -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; - -public interface VertexType { - - VertexFormat getFormat(); - - void copyInto(ByteBuffer buffer, VertexList reader); - - default int getStride() { - return getFormat().getStride(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexWriterUnsafe.java new file mode 100644 index 000000000..5ad8bd70c --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexWriterUnsafe.java @@ -0,0 +1,41 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.vertex.VertexList; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.api.vertex.VertexWriter; + +public abstract class VertexWriterUnsafe implements VertexWriter { + + public final V type; + protected final ByteBuffer buffer; + private int totalVertices; + private int writeVertex; + protected long ptr; + + protected VertexWriterUnsafe(V type, ByteBuffer buffer) { + this.type = type; + this.buffer = buffer; + this.ptr = MemoryUtil.memAddress(buffer); + } + + protected void advance() { + writeVertex++; + if (writeVertex > totalVertices) totalVertices = writeVertex; + } + + @Override + public void seekToVertex(int vertex) { + buffer.position(type.byteOffset(vertex)); + writeVertex = vertex; + ptr = MemoryUtil.memAddress(buffer); + } + + @Override + public VertexList intoReader() { + return type.createReader(buffer, totalVertices); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/package-info.java b/src/main/java/com/jozufozu/flywheel/core/vertex/package-info.java new file mode 100644 index 000000000..23a097290 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.core.vertex; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; From 80d41a76af5e2ee9cc2d33737e30da472fb81788 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 22 Dec 2021 14:39:34 -0800 Subject: [PATCH 12/22] GlBuffer tracks its size - GlVertexArray#enableArrays --- .../flywheel/backend/gl/GlVertexArray.java | 5 ++ .../flywheel/backend/gl/buffer/GlBuffer.java | 74 ++++++++++++++----- .../backend/gl/buffer/MappedGlBuffer.java | 4 +- .../backend/gl/buffer/PersistentGlBuffer.java | 6 +- .../instancing/instancing/GPUInstancer.java | 23 +++--- .../instancing/InstancedMaterialGroup.java | 9 ++- .../backend/model/ArrayModelRenderer.java | 3 +- .../flywheel/backend/model/ModelPool.java | 20 ++--- .../flywheel/backend/model/VBOModel.java | 6 +- .../flywheel/core/FullscreenQuad.java | 4 +- 10 files changed, 93 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java index 9053539e6..c75e1e117 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.backend.gl; +import com.jozufozu.flywheel.util.AttribUtil; import com.mojang.blaze3d.platform.GlStateManager; public class GlVertexArray extends GlObject { @@ -18,4 +19,8 @@ public class GlVertexArray extends GlObject { protected void deleteInternal(int handle) { GlStateManager._glDeleteVertexArrays(handle); } + + public void enableArrays(int count) { + AttribUtil.enableArrays(count); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java index f764f1dec..2c2e31e25 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java @@ -9,13 +9,6 @@ import com.jozufozu.flywheel.backend.gl.GlObject; public abstract class GlBuffer extends GlObject { - protected final GlBufferType type; - - public GlBuffer(GlBufferType type) { - _create(); - this.type = type; - } - /** * Request a Persistent mapped buffer. * @@ -35,10 +28,63 @@ public abstract class GlBuffer extends GlObject { } } - public GlBufferType getBufferTarget() { - return type; + protected final GlBufferType type; + + /** + * The size (in bytes) of the buffer on the GPU. + */ + protected long capacity; + + /** + * How much extra room to give the buffer when we reallocate. + */ + protected int growthMargin; + + public GlBuffer(GlBufferType type) { + _create(); + this.type = type; } + public void setGrowthMargin(int growthMargin) { + this.growthMargin = growthMargin; + } + + public long getCapacity() { + return capacity; + } + + public MappedBuffer getBuffer() { + return getBuffer(0, capacity); + } + + public abstract MappedBuffer getBuffer(long offset, long length); + + /** + * Ensure that the buffer has at least enough room to store size bytes. + * + * @return true if the buffer grew. + */ + public boolean ensureCapacity(long size) { + if (size > capacity) { + capacity = size + growthMargin; + alloc(capacity); + return true; + } + + return false; + } + + /** + * Call this after all draw calls using this buffer are complete. + */ + public void doneForThisFrame() { + + } + + protected abstract void alloc(long size); + + public abstract void upload(ByteBuffer directBuffer); + public void bind() { type.bind(handle()); } @@ -47,16 +93,6 @@ public abstract class GlBuffer extends GlObject { type.unbind(); } - public void doneForThisFrame() { - - } - - public abstract void alloc(long size); - - public abstract void upload(ByteBuffer directBuffer); - - public abstract MappedBuffer getBuffer(int offset, int length); - protected void _create() { setHandle(GL20.glGenBuffers()); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedGlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedGlBuffer.java index 8df8ebea0..aef6e8ce5 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedGlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedGlBuffer.java @@ -21,7 +21,7 @@ public class MappedGlBuffer extends GlBuffer implements Mappable { this.usage = usage; } - public void alloc(long size) { + protected void alloc(long size) { GL15.glBufferData(type.glEnum, size, usage.glEnum); } @@ -29,7 +29,7 @@ public class MappedGlBuffer extends GlBuffer implements Mappable { GL15.glBufferData(type.glEnum, directBuffer, usage.glEnum); } - public MappedBuffer getBuffer(int offset, int length) { + public MappedBuffer getBuffer(long offset, long length) { ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, offset, length, GL30.GL_MAP_WRITE_BIT); if (byteBuffer == null) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java index 86fb3adbf..79f6148ef 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java @@ -35,7 +35,7 @@ public class PersistentGlBuffer extends GlBuffer implements Mappable { } @Override - public void alloc(long size) { + protected void alloc(long size) { this.size = size; if (buffer != null) { @@ -71,11 +71,11 @@ public class PersistentGlBuffer extends GlBuffer implements Mappable { } @Override - public MappedBuffer getBuffer(int offset, int length) { + public MappedBuffer getBuffer(long offset, long length) { fence.waitSync(); - buffer.position(offset); + buffer.position((int) offset); return buffer; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index ed92586cf..ae9cb845d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.backend.instancing.instancing; +import org.lwjgl.system.MemoryUtil; + import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.Instanced; @@ -10,12 +12,10 @@ import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; -import com.jozufozu.flywheel.backend.gl.error.GlError; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.model.BufferedModel; import com.jozufozu.flywheel.backend.model.ModelAllocator; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.util.AttribUtil; public class GPUInstancer extends AbstractInstancer { @@ -26,7 +26,6 @@ public class GPUInstancer extends AbstractInstancer { private BufferedModel model; private GlVertexArray vao; private GlBuffer instanceVBO; - private int glBufferSize = -1; private int glInstanceCount = 0; private boolean deleted; private boolean initialized; @@ -49,14 +48,11 @@ public class GPUInstancer extends AbstractInstancer { if (invalid()) return; vao.bind(); - GlError.pollAndThrow(() -> modelData.name() + "_bind"); renderSetup(); - GlError.pollAndThrow(() -> modelData.name() + "_setup"); if (glInstanceCount > 0) { model.drawInstances(glInstanceCount); - GlError.pollAndThrow(() -> modelData.name() + "_draw"); } // persistent mapping sync point @@ -83,7 +79,8 @@ public class GPUInstancer extends AbstractInstancer { vao.bind(); instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER); - AttribUtil.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount()); + instanceVBO.setGrowthMargin(instanceFormat.getStride() * 16); + vao.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount()); } public boolean isInitialized() { @@ -135,10 +132,10 @@ public class GPUInstancer extends AbstractInstancer { private void clearBufferTail() { int size = data.size(); final int offset = size * instanceFormat.getStride(); - final int length = glBufferSize - offset; + final long length = instanceVBO.getCapacity() - offset; if (length > 0) { try (MappedBuffer buf = instanceVBO.getBuffer(offset, length)) { - buf.putByteArray(new byte[length]); + MemoryUtil.memSet(MemoryUtil.memAddress(buf.unwrap()), 0, length); } catch (Exception e) { Flywheel.log.error("Error clearing buffer tail:", e); } @@ -150,7 +147,7 @@ public class GPUInstancer extends AbstractInstancer { if (size <= 0) return; - try (MappedBuffer mapped = instanceVBO.getBuffer(0, glBufferSize)) { + try (MappedBuffer mapped = instanceVBO.getBuffer()) { final StructWriter writer = instancedType.getWriter(mapped); @@ -170,11 +167,9 @@ public class GPUInstancer extends AbstractInstancer { int size = this.data.size(); int stride = instanceFormat.getStride(); int requiredSize = size * stride; - if (requiredSize > glBufferSize) { - glBufferSize = requiredSize + stride * 16; - instanceVBO.alloc(glBufferSize); + if (instanceVBO.ensureCapacity(requiredSize)) { - try (MappedBuffer buffer = instanceVBO.getBuffer(0, glBufferSize)) { + try (MappedBuffer buffer = instanceVBO.getBuffer()) { StructWriter writer = instancedType.getWriter(buffer); for (D datum : data) { writer.write(datum); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java index f0a432b2d..8938686f1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java @@ -59,7 +59,10 @@ public class InstancedMaterialGroup

implements MaterialG .values(); // initialize all uninitialized instancers... - instancers.forEach(GPUInstancer::init); + for (GPUInstancer gpuInstancer : instancers) { + gpuInstancer.init(); + } + if (material.allocator instanceof ModelPool pool) { // ...and then flush the model arena in case anything was marked for upload pool.flush(); @@ -74,7 +77,9 @@ public class InstancedMaterialGroup

implements MaterialG setup(program); - instancers.forEach(GPUInstancer::render); + for (GPUInstancer instancer : instancers) { + instancer.render(); + } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java index 97cfc5da7..0a5185bd9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java @@ -36,12 +36,11 @@ public class ArrayModelRenderer extends ModelRenderer { vao = new GlVertexArray(); vao.bind(); + vao.enableArrays(this.model.getAttributeCount()); // bind the model's vbo to our vao this.model.setupState(); - AttribUtil.enableArrays(this.model.getAttributeCount()); - GlVertexArray.unbind(); this.model.clearState(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java index c93fe945c..feb5815ab 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java @@ -25,7 +25,6 @@ public class ModelPool implements ModelAllocator { private final List pendingUpload = new ArrayList<>(); private final GlBuffer vbo; - private int bufferSize; private int vertices; @@ -34,12 +33,13 @@ public class ModelPool implements ModelAllocator { public ModelPool(VertexType vertexType, int initialSize) { this.vertexType = vertexType; - bufferSize = vertexType.getStride() * initialSize; + int stride = vertexType.getStride(); vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); vbo.bind(); - vbo.alloc(bufferSize); + vbo.ensureCapacity((long) stride * initialSize); + vbo.setGrowthMargin(stride * 64); vbo.unbind(); } @@ -104,19 +104,11 @@ public class ModelPool implements ModelAllocator { * @return true if the buffer was reallocated */ private boolean realloc() { - int neededSize = vertices * vertexType.getStride(); - if (neededSize > bufferSize) { - bufferSize = neededSize + 128; - vbo.alloc(bufferSize); - - return true; - } - - return false; + return vbo.ensureCapacity((long) vertices * vertexType.getStride()); } private void uploadAll() { - try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { + try (MappedBuffer buffer = vbo.getBuffer()) { VertexWriter writer = vertexType.createWriter(buffer.unwrap()); int vertices = 0; @@ -134,7 +126,7 @@ public class ModelPool implements ModelAllocator { } private void uploadPending() { - try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { + try (MappedBuffer buffer = vbo.getBuffer()) { VertexWriter writer = vertexType.createWriter(buffer.unwrap()); for (PooledModel model : pendingUpload) { buffer(writer, model); diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java index fee075c2a..06c8d1066 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java @@ -29,10 +29,10 @@ public class VBOModel implements BufferedModel { vbo.bind(); // allocate the buffer on the gpu - vbo.alloc(model.size()); + vbo.ensureCapacity(model.size()); - // mirror it in system memory so we can write to it, and upload our model. - try (MappedBuffer buffer = vbo.getBuffer(0, model.size())) { + // mirror it in system memory, so we can write to it, and upload our model. + try (MappedBuffer buffer = vbo.getBuffer()) { model.writeInto(buffer.unwrap()); } catch (Exception e) { Flywheel.log.error(String.format("Error uploading model '%s':", model.name()), e); diff --git a/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java b/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java index 1436dab6c..d319a2656 100644 --- a/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java +++ b/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java @@ -32,8 +32,8 @@ public class FullscreenQuad { private FullscreenQuad() { vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); vbo.bind(); - vbo.alloc(bufferSize); - try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { + vbo.ensureCapacity(bufferSize); + try (MappedBuffer buffer = vbo.getBuffer()) { buffer.putFloatArray(vertices); } catch (Exception e) { Flywheel.log.error("Could not create fullscreen quad.", e); From 99a50631cbaa9d937774eb0eaeaef3a23a49ae29 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 22 Dec 2021 16:11:56 -0800 Subject: [PATCH 13/22] Fix diffuse lighting in batching engine - Complete hack that definitely won't come back to bite me - Also slighting change some unsafe writing stuff --- .../instancing/batching/BatchingEngine.java | 11 ++++++ .../core/materials/BasicWriterUnsafe.java | 14 ++++---- .../materials/model/ModelWriterUnsafe.java | 7 ++-- .../oriented/OrientedWriterUnsafe.java | 22 ++++++------ .../flywheel/mixin/matrix/Matrix3fMixin.java | 20 +++++------ .../flywheel/mixin/matrix/Matrix4fMixin.java | 34 +++++++++---------- .../jozufozu/flywheel/util/WriteUnsafe.java | 2 +- 7 files changed, 60 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index 7af1eb14a..c8ebbed58 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -9,9 +9,11 @@ import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.event.RenderLayerEvent; +import com.mojang.blaze3d.platform.Lighting; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Matrix4f; import net.minecraft.client.Camera; import net.minecraft.client.renderer.MultiBufferSource; @@ -60,6 +62,15 @@ public class BatchingEngine implements Engine, MultiBufferSource { stack.popPose(); + // FIXME: this probably breaks some vanilla stuff but it works much better for flywheel + Matrix4f mat = new Matrix4f(); + mat.setIdentity(); + if (event.getWorld().effects().constantAmbientLight()) { + Lighting.setupNetherLevel(mat); + } else { + Lighting.setupLevel(mat); + } + // TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders // into the RenderBuffers from context. buffers.forEach((type, builder) -> type.end(builder, 0, 0, 0)); diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/BasicWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/materials/BasicWriterUnsafe.java index 1ece742b2..0ce2f7a75 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/BasicWriterUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/BasicWriterUnsafe.java @@ -14,12 +14,12 @@ public abstract class BasicWriterUnsafe extends UnsafeBuffe @Override protected void writeInternal(D d) { - long addr = writePointer; - MemoryUtil.memPutByte(addr, (byte) (d.blockLight << 4)); - MemoryUtil.memPutByte(addr + 1, (byte) (d.skyLight << 4)); - MemoryUtil.memPutByte(addr + 2, d.r); - MemoryUtil.memPutByte(addr + 3, d.g); - MemoryUtil.memPutByte(addr + 4, d.b); - MemoryUtil.memPutByte(addr + 5, d.a); + long ptr = writePointer; + MemoryUtil.memPutByte(ptr, (byte) (d.blockLight << 4)); + MemoryUtil.memPutByte(ptr + 1, (byte) (d.skyLight << 4)); + MemoryUtil.memPutByte(ptr + 2, d.r); + MemoryUtil.memPutByte(ptr + 3, d.g); + MemoryUtil.memPutByte(ptr + 4, d.b); + MemoryUtil.memPutByte(ptr + 5, d.a); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java index ba842aabd..6fac04507 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java @@ -14,10 +14,9 @@ public class ModelWriterUnsafe extends BasicWriterUnsafe { @Override protected void writeInternal(ModelData d) { super.writeInternal(d); - long addr = writePointer + 6; + long ptr = writePointer + 6; - ((WriteUnsafe) (Object) d.model).writeUnsafe(addr); - addr += 4 * 16; - ((WriteUnsafe) (Object) d.normal).writeUnsafe(addr); + ((WriteUnsafe) (Object) d.model).writeUnsafe(ptr); + ((WriteUnsafe) (Object) d.normal).writeUnsafe(ptr + 4 * 16); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedWriterUnsafe.java index 201faf6eb..f7db693a3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedWriterUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedWriterUnsafe.java @@ -13,18 +13,18 @@ public class OrientedWriterUnsafe extends BasicWriterUnsafe { @Override protected void writeInternal(OrientedData d) { - long addr = writePointer; + long ptr = writePointer; super.writeInternal(d); - MemoryUtil.memPutFloat(addr + 6, d.posX); - MemoryUtil.memPutFloat(addr + 10, d.posY); - MemoryUtil.memPutFloat(addr + 14, d.posZ); - MemoryUtil.memPutFloat(addr + 18, d.pivotX); - MemoryUtil.memPutFloat(addr + 22, d.pivotY); - MemoryUtil.memPutFloat(addr + 26, d.pivotZ); - MemoryUtil.memPutFloat(addr + 30, d.qX); - MemoryUtil.memPutFloat(addr + 34, d.qY); - MemoryUtil.memPutFloat(addr + 38, d.qZ); - MemoryUtil.memPutFloat(addr + 42, d.qW); + MemoryUtil.memPutFloat(ptr + 6, d.posX); + MemoryUtil.memPutFloat(ptr + 10, d.posY); + MemoryUtil.memPutFloat(ptr + 14, d.posZ); + MemoryUtil.memPutFloat(ptr + 18, d.pivotX); + MemoryUtil.memPutFloat(ptr + 22, d.pivotY); + MemoryUtil.memPutFloat(ptr + 26, d.pivotZ); + MemoryUtil.memPutFloat(ptr + 30, d.qX); + MemoryUtil.memPutFloat(ptr + 34, d.qY); + MemoryUtil.memPutFloat(ptr + 38, d.qZ); + MemoryUtil.memPutFloat(ptr + 42, d.qW); } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java index 1c5449100..fc7dd333c 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java @@ -27,16 +27,16 @@ public abstract class Matrix3fMixin implements WriteUnsafe, WriteSafe { @Shadow protected float m22; @Override - public void writeUnsafe(long addr) { - MemoryUtil.memPutFloat(addr, m00); - MemoryUtil.memPutFloat(addr += 4L, m10); - MemoryUtil.memPutFloat(addr += 4L, m20); - MemoryUtil.memPutFloat(addr += 4L, m01); - MemoryUtil.memPutFloat(addr += 4L, m11); - MemoryUtil.memPutFloat(addr += 4L, m21); - MemoryUtil.memPutFloat(addr += 4L, m02); - MemoryUtil.memPutFloat(addr += 4L, m12); - MemoryUtil.memPutFloat(addr += 4L, m22); + public void writeUnsafe(long ptr) { + MemoryUtil.memPutFloat(ptr, m00); + MemoryUtil.memPutFloat(ptr + 4, m10); + MemoryUtil.memPutFloat(ptr + 8, m20); + MemoryUtil.memPutFloat(ptr + 12, m01); + MemoryUtil.memPutFloat(ptr + 16, m11); + MemoryUtil.memPutFloat(ptr + 20, m21); + MemoryUtil.memPutFloat(ptr + 24, m02); + MemoryUtil.memPutFloat(ptr + 28, m12); + MemoryUtil.memPutFloat(ptr + 32, m22); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java index 33bc6da8f..8d98d502e 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java @@ -34,23 +34,23 @@ public abstract class Matrix4fMixin implements WriteUnsafe, WriteSafe { @Shadow protected float m33; @Override - public void writeUnsafe(long addr) { - MemoryUtil.memPutFloat(addr, m00); - MemoryUtil.memPutFloat(addr += 4L, m10); - MemoryUtil.memPutFloat(addr += 4L, m20); - MemoryUtil.memPutFloat(addr += 4L, m30); - MemoryUtil.memPutFloat(addr += 4L, m01); - MemoryUtil.memPutFloat(addr += 4L, m11); - MemoryUtil.memPutFloat(addr += 4L, m21); - MemoryUtil.memPutFloat(addr += 4L, m31); - MemoryUtil.memPutFloat(addr += 4L, m02); - MemoryUtil.memPutFloat(addr += 4L, m12); - MemoryUtil.memPutFloat(addr += 4L, m22); - MemoryUtil.memPutFloat(addr += 4L, m32); - MemoryUtil.memPutFloat(addr += 4L, m03); - MemoryUtil.memPutFloat(addr += 4L, m13); - MemoryUtil.memPutFloat(addr += 4L, m23); - MemoryUtil.memPutFloat(addr += 4L, m33); + public void writeUnsafe(long ptr) { + MemoryUtil.memPutFloat(ptr, m00); + MemoryUtil.memPutFloat(ptr + 4, m10); + MemoryUtil.memPutFloat(ptr + 8, m20); + MemoryUtil.memPutFloat(ptr + 12, m30); + MemoryUtil.memPutFloat(ptr + 16, m01); + MemoryUtil.memPutFloat(ptr + 20, m11); + MemoryUtil.memPutFloat(ptr + 24, m21); + MemoryUtil.memPutFloat(ptr + 28, m31); + MemoryUtil.memPutFloat(ptr + 32, m02); + MemoryUtil.memPutFloat(ptr + 36, m12); + MemoryUtil.memPutFloat(ptr + 40, m22); + MemoryUtil.memPutFloat(ptr + 44, m32); + MemoryUtil.memPutFloat(ptr + 48, m03); + MemoryUtil.memPutFloat(ptr + 52, m13); + MemoryUtil.memPutFloat(ptr + 56, m23); + MemoryUtil.memPutFloat(ptr + 60, m33); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/util/WriteUnsafe.java b/src/main/java/com/jozufozu/flywheel/util/WriteUnsafe.java index 7d963ee5c..1b4b6e985 100644 --- a/src/main/java/com/jozufozu/flywheel/util/WriteUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/util/WriteUnsafe.java @@ -5,5 +5,5 @@ public interface WriteUnsafe { /** * Write the contents of this object into sequential memory starting at the given address. */ - void writeUnsafe(long addr); + void writeUnsafe(long ptr); } From ee2b418ef71e67a595b1b3fa57bbfabdf6bbc836 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 22 Dec 2021 21:35:48 -0800 Subject: [PATCH 14/22] Better config/backend to account for multiple engines - Rename probably too many things - Needs to be tested with Optifine --- .../java/com/jozufozu/flywheel/Flywheel.java | 3 + .../jozufozu/flywheel/backend/Backend.java | 72 ++++++------------ .../com/jozufozu/flywheel/backend/Loader.java | 42 +++++------ .../backend/instancing/InstanceManager.java | 21 ++---- .../backend/instancing/InstanceWorld.java | 2 +- .../instancing/InstancedRenderDispatcher.java | 10 +-- .../flywheel/config/BooleanConfig.java | 24 ------ .../flywheel/config/EngineArgument.java | 69 +++++++++++++++++ .../flywheel/config/EngineConfigCommand.java | 6 +- .../jozufozu/flywheel/config/FlwCommands.java | 3 +- .../jozufozu/flywheel/config/FlwConfig.java | 10 +-- .../jozufozu/flywheel/config/FlwEngine.java | 75 ++++++++++++------- .../config/SConfigureEnginePacket.java | 4 +- .../flywheel/config/package-info.java | 6 ++ .../core/crumbling/CrumblingRenderer.java | 6 +- .../mixin/CancelEntityRenderMixin.java | 3 +- .../mixin/ChunkRebuildHooksMixin.java | 2 +- .../flywheel/mixin/RenderHooksMixin.java | 5 +- 18 files changed, 195 insertions(+), 168 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/config/EngineArgument.java create mode 100644 src/main/java/com/jozufozu/flywheel/config/package-info.java diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index 3f8f8c1cd..956a6192b 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -3,10 +3,12 @@ package com.jozufozu.flywheel; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import com.jozufozu.flywheel.config.EngineArgument; import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwPackets; +import net.minecraft.commands.synchronization.ArgumentTypes; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; @@ -39,5 +41,6 @@ public class Flywheel { private void setup(final FMLCommonSetupEvent event) { FlwPackets.registerPackets(); + ArgumentTypes.register("flywheel:engine", EngineArgument.class, EngineArgument.SERIALIZER); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 5b65ce868..1d5f931eb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -17,13 +17,11 @@ import com.jozufozu.flywheel.api.FlywheelWorld; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwEngine; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; @@ -32,20 +30,18 @@ public class Backend { public static final Logger log = LogManager.getLogger(Backend.class); protected static final Backend INSTANCE = new Backend(); - private FlwEngine engine; - public static Backend getInstance() { return INSTANCE; } - public final Loader loader; + private FlwEngine engine; public GLCapabilities capabilities; public GlCompat compat; - private boolean instancedArrays; private boolean enabled; + public final Loader loader; private final List> contexts = new ArrayList<>(); private final Map> materialRegistry = new HashMap<>(); private final Map programSpecRegistry = new HashMap<>(); @@ -61,16 +57,11 @@ public class Backend { * (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use. */ public String getBackendDescriptor() { - if (enabled) { - ClientLevel level = Minecraft.getInstance().level; + return engine.getProperName(); + } - if (level == null) { - return "Invalid"; - } - return InstancedRenderDispatcher.getEngineName(level); - } - - return "Disabled"; + public FlwEngine getEngine() { + return engine; } /** @@ -111,41 +102,22 @@ public class Backend { return programSpecRegistry.get(name); } - public boolean available() { - return canUseVBOs(); - } - - public boolean canUseInstancing() { - return enabled && instancedArrays; - } - - public boolean canUseVBOs() { - return enabled && gl20(); - } - - public boolean gl33() { - return capabilities.OpenGL33; - } - - public boolean gl20() { - return capabilities.OpenGL20; - } - public void refresh() { OptifineHandler.refresh(); + boolean usingShaders = OptifineHandler.usingShaders(); + capabilities = GL.createCapabilities(); compat = new GlCompat(capabilities); - instancedArrays = compat.instancedArraysSupported(); + engine = FlwConfig.get() + .getEngine(); - FlwConfig config = FlwConfig.get(); - enabled = config.enabled() && !OptifineHandler.usingShaders(); - engine = config.client.engine.get(); - } - - public boolean canUseInstancing(@Nullable Level world) { - return canUseInstancing() && isFlywheelWorld(world); + enabled = switch (engine) { + case OFF -> false; + case BATCHING -> true; + case INSTANCING -> !usingShaders && compat.instancedArraysSupported(); + }; } public Collection> allMaterials() { @@ -160,6 +132,14 @@ public class Backend { return contexts; } + public static boolean isOn() { + return getInstance().enabled; + } + + public static boolean canUseInstancing(@Nullable Level world) { + return isOn() && isFlywheelWorld(world); + } + /** * Used to avoid calling Flywheel functions on (fake) worlds that don't specifically support it. */ @@ -184,7 +164,7 @@ public class Backend { /** * INTERNAL USE ONLY */ - public void _clearContexts() { + void _clearContexts() { GameStateRegistry.clear(); programSpecRegistry.clear(); contexts.forEach(ShaderContext::delete); @@ -194,8 +174,4 @@ public class Backend { public static void init() { } - - public FlwEngine getEngine() { - return engine; - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 92b675943..9bf8d3b80 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -64,36 +64,34 @@ public class Loader implements ResourceManagerReloadListener { public void onResourceManagerReload(ResourceManager manager) { backend.refresh(); - if (backend.gl20()) { - shouldCrash = false; - backend._clearContexts(); + shouldCrash = false; + backend._clearContexts(); - Resolver.INSTANCE.invalidate(); - ModLoader.get() - .postEvent(new GatherContextEvent(backend, firstLoad)); + Resolver.INSTANCE.invalidate(); + ModLoader.get() + .postEvent(new GatherContextEvent(backend, firstLoad)); - ShaderSources sources = new ShaderSources(manager); + ShaderSources sources = new ShaderSources(manager); - loadProgramSpecs(manager); + loadProgramSpecs(manager); - Resolver.INSTANCE.resolve(sources); + Resolver.INSTANCE.resolve(sources); - for (ShaderContext context : backend.allContexts()) { - context.load(); - } + for (ShaderContext context : backend.allContexts()) { + context.load(); + } - if (shouldCrash) { - throw new ShaderLoadingException("Could not load all shaders, see log for details"); - } + if (shouldCrash) { + throw new ShaderLoadingException("Could not load all shaders, see log for details"); + } - Backend.log.info("Loaded all shader programs."); + Backend.log.info("Loaded all shader programs."); - ClientLevel world = Minecraft.getInstance().level; - if (Backend.isFlywheelWorld(world)) { - // TODO: looks like it might be good to have another event here - InstancedRenderDispatcher.resetInstanceWorld(world); - CrumblingRenderer.reset(); - } + ClientLevel world = Minecraft.getInstance().level; + if (Backend.isFlywheelWorld(world)) { + // TODO: looks like it might be good to have another event here + InstancedRenderDispatcher.resetInstanceWorld(world); + CrumblingRenderer.reset(); } firstLoad = false; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 758168b9b..449c04862 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -154,8 +154,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } public void add(T obj) { - if (!Backend.getInstance() - .canUseInstancing()) return; + if (!Backend.isOn()) return; if (canInstance(obj)) { addInternal(obj); @@ -163,8 +162,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } public void queueAdd(T obj) { - if (!Backend.getInstance() - .canUseInstancing()) return; + if (!Backend.isOn()) return; synchronized (queuedAdditions) { queuedAdditions.add(obj); @@ -172,8 +170,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } public void queueUpdate(T obj) { - if (!Backend.getInstance() - .canUseInstancing()) return; + if (!Backend.isOn()) return; synchronized (queuedUpdates) { queuedUpdates.add(obj); } @@ -191,8 +188,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift * @param obj the object to update. */ public void update(T obj) { - if (!Backend.getInstance() - .canUseInstancing()) return; + if (!Backend.isOn()) return; if (canInstance(obj)) { AbstractInstance instance = getInstance(obj); @@ -213,8 +209,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } public void remove(T obj) { - if (!Backend.getInstance() - .canUseInstancing()) return; + if (!Backend.isOn()) return; if (canInstance(obj)) { AbstractInstance instance = getInstance(obj); @@ -231,8 +226,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift @Nullable protected AbstractInstance getInstance(I obj) { - if (!Backend.getInstance() - .canUseInstancing()) return null; + if (!Backend.isOn()) return null; return instances.get(obj); } @@ -291,8 +285,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } protected void addInternal(T obj) { - if (!Backend.getInstance() - .canUseInstancing()) return; + if (!Backend.isOn()) return; AbstractInstance instance = instances.get(obj); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 1b85c8ed6..a913b442d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -40,7 +40,7 @@ public class InstanceWorld { .getEngine(); switch (engine) { - case GL33 -> { + case INSTANCING -> { InstancingEngine manager = InstancingEngine.builder(Contexts.WORLD) .build(this.taskEngine); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index c07a1d325..931908b3f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -41,10 +41,6 @@ public class InstancedRenderDispatcher { getEntities(entity.level).queueUpdate(entity); } - public static String getEngineName(LevelAccessor world) { - return instanceWorlds.get(world).engine.getName(); - } - public static InstanceManager getTiles(LevelAccessor world) { return instanceWorlds.get(world) .getTileEntityInstanceManager(); @@ -81,8 +77,7 @@ public class InstancedRenderDispatcher { if (event.layer == null) return; ClientLevel world = event.getWorld(); - if (!Backend.getInstance() - .canUseInstancing(world)) return; + if (!Backend.canUseInstancing(world)) return; instanceWorlds.get(world).renderLayer(event); } @@ -90,8 +85,7 @@ public class InstancedRenderDispatcher { @SubscribeEvent public static void onReloadRenderers(ReloadRenderersEvent event) { ClientLevel world = event.getWorld(); - if (Backend.getInstance() - .canUseInstancing() && world != null) { + if (Backend.isOn() && world != null) { resetInstanceWorld(world); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java b/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java index 183941ac2..78f611990 100644 --- a/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java @@ -17,7 +17,6 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; public enum BooleanConfig { - ENGINE(() -> BooleanConfig::enabled), NORMAL_OVERLAY(() -> BooleanConfig::normalOverlay), ; @@ -52,29 +51,6 @@ public enum BooleanConfig { return null; } - @OnlyIn(Dist.CLIENT) - private static void enabled(BooleanDirective state) { - LocalPlayer player = Minecraft.getInstance().player; - if (player == null || state == null) return; - - if (state == BooleanDirective.DISPLAY) { - Component text = new TextComponent("Flywheel renderer is currently: ").append(boolToText(FlwConfig.get().enabled())); - player.displayClientMessage(text, false); - return; - } - - boolean enabled = state.get(); - boolean cannotUse = OptifineHandler.usingShaders() && enabled; - - FlwConfig.get().client.enabled.set(enabled); - - Component text = boolToText(FlwConfig.get().enabled()).append(new TextComponent(" Flywheel renderer").withStyle(ChatFormatting.WHITE)); - Component error = new TextComponent("Flywheel renderer does not support Optifine Shaders").withStyle(ChatFormatting.RED); - - player.displayClientMessage(cannotUse ? error : text, false); - Backend.reloadWorldRenderers(); - } - @OnlyIn(Dist.CLIENT) private static void normalOverlay(BooleanDirective state) { LocalPlayer player = Minecraft.getInstance().player; diff --git a/src/main/java/com/jozufozu/flywheel/config/EngineArgument.java b/src/main/java/com/jozufozu/flywheel/config/EngineArgument.java new file mode 100644 index 000000000..f24a758cb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/config/EngineArgument.java @@ -0,0 +1,69 @@ +package com.jozufozu.flywheel.config; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import com.google.gson.JsonObject; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; + +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.synchronization.ArgumentSerializer; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.TranslatableComponent; + +public class EngineArgument implements ArgumentType { + + public static final EngineArgument INSTANCE = new EngineArgument(); + public static final Serializer SERIALIZER = new Serializer(); + + private static final Dynamic2CommandExceptionType INVALID = new Dynamic2CommandExceptionType((found, constants) -> { + return new TranslatableComponent("commands.forge.arguments.enum.invalid", constants, found); + }); + + private EngineArgument() { + } + + @Override + public FlwEngine parse(StringReader reader) throws CommandSyntaxException { + String string = reader.readUnquotedString(); + + FlwEngine engine = FlwEngine.byName(string); + + if (engine == null) { + throw INVALID.createWithContext(reader, string, FlwEngine.validNames()); + } + + return engine; + } + + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return SharedSuggestionProvider.suggest(FlwEngine.validNames(), builder); + } + + @Override + public Collection getExamples() { + return FlwEngine.validNames(); + } + + public static class Serializer implements ArgumentSerializer { + private Serializer() { + } + + public void serializeToNetwork(EngineArgument argument, FriendlyByteBuf buffer) { + } + + public EngineArgument deserializeFromNetwork(FriendlyByteBuf buffer) { + return INSTANCE; + } + + public void serializeToJson(EngineArgument argument, JsonObject json) { + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java b/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java index a5ab057ce..e8aefc36f 100644 --- a/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java +++ b/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java @@ -10,15 +10,15 @@ import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.server.command.EnumArgument; public class EngineConfigCommand { - public ArgumentBuilder register() { - return Commands.literal("engine") + public static ArgumentBuilder register() { + return Commands.literal("backend") .executes(context -> { ServerPlayer player = context.getSource() .getPlayerOrException(); FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket()); return Command.SINGLE_SUCCESS; }) - .then(Commands.argument("type", EnumArgument.enumArgument(FlwEngine.class)) + .then(Commands.argument("type", EngineArgument.INSTANCE) .executes(context -> { FlwEngine type = context.getArgument("type", FlwEngine.class); diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 3cee5f1eb..af8f9648b 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -15,9 +15,8 @@ public class FlwCommands { .getDispatcher(); dispatcher.register(Commands.literal("flywheel") - .then(new BooleanConfigCommand("backend", BooleanConfig.ENGINE).register()) .then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register()) - .then(new EngineConfigCommand().register()) + .then(EngineConfigCommand.register()) ); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java index 1efdba072..a7457e70d 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java @@ -26,8 +26,8 @@ public class FlwConfig { return INSTANCE; } - public boolean enabled() { - return client.enabled.get(); + public FlwEngine getEngine() { + return client.engine.get(); } public boolean debugNormals() { @@ -38,17 +38,13 @@ public class FlwConfig { } public static class ClientConfig { - public final BooleanValue enabled; public final ForgeConfigSpec.EnumValue engine; public final BooleanValue debugNormals; public ClientConfig(ForgeConfigSpec.Builder builder) { - enabled = builder.comment("Enable or disable the entire engine") - .define("enabled", true); - engine = builder.comment("Enable or disable the entire engine") - .defineEnum("backend", FlwEngine.GL33); + .defineEnum("backend", FlwEngine.INSTANCING); debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal") .define("debugNormals", false); diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java b/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java index 45a09fb45..2692c63e1 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java @@ -1,9 +1,12 @@ package com.jozufozu.flywheel.config; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + import javax.annotation.Nullable; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.OptifineHandler; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; @@ -13,15 +16,50 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; public enum FlwEngine { - BATCHING(new TextComponent("Batching").withStyle(ChatFormatting.BLUE)), - GL33(new TextComponent("GL 3.3 Instanced Arrays").withStyle(ChatFormatting.GREEN)), - + OFF("off", "Off", new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED)), + BATCHING("batching", "Parallel Batching", new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN)), + INSTANCING("instancing", "GL33 Instanced Arrays", new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN)), ; - private final Component name; + private static final Map lookup; - FlwEngine(Component name) { - this.name = name; + static { + lookup = new HashMap<>(); + for (FlwEngine value : values()) { + lookup.put(value.shortName, value); + } + } + + private final Component message; + private final String shortName; + private final String properName; + + FlwEngine(String shortName, String properName, Component message) { + this.shortName = shortName; + this.properName = properName; + this.message = message; + } + + public String getProperName() { + return properName; + } + + public void encode(FriendlyByteBuf buffer) { + buffer.writeByte(this.ordinal()); + } + + public static void handle(@Nullable FlwEngine type) { + LocalPlayer player = Minecraft.getInstance().player; + if (player == null) return; + + if (type != null) { + FlwConfig.get().client.engine.set(type); + + player.displayClientMessage(type.message, false); + Backend.reloadWorldRenderers(); + } else { + player.displayClientMessage(FlwConfig.get().getEngine().message, false); + } } @Nullable @@ -33,25 +71,12 @@ public enum FlwEngine { return values()[b]; } - public void encode(FriendlyByteBuf buffer) { - buffer.writeByte(this.ordinal()); + @Nullable + public static FlwEngine byName(String name) { + return lookup.get(name); } - public void switchTo() { - LocalPlayer player = Minecraft.getInstance().player; - if (player == null) return; - -// if (state == BooleanDirective.DISPLAY) { -// Component text = new TextComponent("Flywheel renderer is currently: ").append(boolToText(FlwConfig.get().enabled())); -// player.displayClientMessage(text, false); -// return; -// } - - FlwConfig.get().client.engine.set(this); - - Component text = new TextComponent("Using ").withStyle(ChatFormatting.WHITE).append(name); - - player.displayClientMessage(text, false); - Backend.reloadWorldRenderers(); + public static Collection validNames() { + return lookup.keySet(); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/SConfigureEnginePacket.java b/src/main/java/com/jozufozu/flywheel/config/SConfigureEnginePacket.java index ac7a4c8e9..d287d7aca 100644 --- a/src/main/java/com/jozufozu/flywheel/config/SConfigureEnginePacket.java +++ b/src/main/java/com/jozufozu/flywheel/config/SConfigureEnginePacket.java @@ -29,9 +29,7 @@ public class SConfigureEnginePacket { } public void execute(Supplier ctx) { - if (type != null) { - type.switchTo(); - } + FlwEngine.handle(type); ctx.get() .setPacketHandled(true); } diff --git a/src/main/java/com/jozufozu/flywheel/config/package-info.java b/src/main/java/com/jozufozu/flywheel/config/package-info.java new file mode 100644 index 000000000..01b8f8ae7 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/config/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.config; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index 29efdc84d..42ae86e71 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -54,8 +54,7 @@ public class CrumblingRenderer { } public static void renderBreaking(RenderLayerEvent event) { - if (!Backend.getInstance() - .canUseInstancing(event.getWorld())) return; + if (!Backend.canUseInstancing(event.getWorld())) return; Int2ObjectMap> activeStages = getActiveStageTiles(event.getWorld()); @@ -120,8 +119,7 @@ public class CrumblingRenderer { @SubscribeEvent public static void onReloadRenderers(ReloadRenderersEvent event) { ClientLevel world = event.getWorld(); - if (Backend.getInstance() - .canUseInstancing() && world != null) { + if (Backend.isOn() && world != null) { reset(); } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/CancelEntityRenderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/CancelEntityRenderMixin.java index 9f101adc4..0e64a35ed 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/CancelEntityRenderMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/CancelEntityRenderMixin.java @@ -23,8 +23,7 @@ public class CancelEntityRenderMixin { @Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;entitiesForRendering()Ljava/lang/Iterable;")) private Iterable filterEntities(ClientLevel world) { Iterable entities = world.entitiesForRendering(); - if (Backend.getInstance() - .canUseInstancing()) { + if (Backend.isOn()) { ArrayList filtered = Lists.newArrayList(entities); diff --git a/src/main/java/com/jozufozu/flywheel/mixin/ChunkRebuildHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/ChunkRebuildHooksMixin.java index e5a75fc54..2ddc15562 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/ChunkRebuildHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/ChunkRebuildHooksMixin.java @@ -23,7 +23,7 @@ public class ChunkRebuildHooksMixin { @Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true) private void addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set set, E be, CallbackInfo ci) { - if (Backend.getInstance().canUseInstancing() && Backend.isFlywheelWorld(be.getLevel())) { + if (Backend.isOn() && Backend.isFlywheelWorld(be.getLevel())) { InstancedRenderRegistry registry = InstancedRenderRegistry.getInstance(); if (registry.canInstance(be.getType())) diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java index 9fd956b00..9e963213e 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.mixin; -import org.lwjgl.opengl.GL20; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -9,7 +8,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.OptifineHandler; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.event.BeginFrameEvent; @@ -74,8 +72,7 @@ public class RenderHooksMixin { @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;checkPoseStack(Lcom/mojang/blaze3d/vertex/PoseStack;)V", ordinal = 2 // after the game renders the breaking overlay normally ), method = "renderLevel") private void renderBlockBreaking(PoseStack stack, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, Camera info, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f p_228426_9_, CallbackInfo ci) { - if (!Backend.getInstance() - .available()) return; + if (!Backend.isOn()) return; Vec3 cameraPos = info.getPosition(); From dc5382b658448b39d61115dfd7045a225c871602 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 22 Dec 2021 22:01:05 -0800 Subject: [PATCH 15/22] Fix models being backwards - Regression in ModelUtil#rotateToFace --- .../jozufozu/flywheel/core/model/ModelUtil.java | 2 +- .../util/transform/MatrixTransformStack.java | 14 ++++++++++++++ .../flywheel/util/transform/TransformStack.java | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java index df17f63c4..921b1f4df 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -99,7 +99,7 @@ public class ModelUtil { PoseStack stack = new PoseStack(); TransformStack.cast(stack) .centre() - .rotateToFace(facing) + .rotateToFace(facing.getOpposite()) .unCentre(); return stack; }; diff --git a/src/main/java/com/jozufozu/flywheel/util/transform/MatrixTransformStack.java b/src/main/java/com/jozufozu/flywheel/util/transform/MatrixTransformStack.java index 2fa562ffa..8a354c80a 100644 --- a/src/main/java/com/jozufozu/flywheel/util/transform/MatrixTransformStack.java +++ b/src/main/java/com/jozufozu/flywheel/util/transform/MatrixTransformStack.java @@ -1,6 +1,8 @@ package com.jozufozu.flywheel.util.transform; import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Matrix3f; +import com.mojang.math.Matrix4f; import com.mojang.math.Quaternion; public class MatrixTransformStack implements TransformStack { @@ -64,4 +66,16 @@ public class MatrixTransformStack implements TransformStack { internal.popPose(); return this; } + + @Override + public TransformStack mulPose(Matrix4f pose) { + internal.last().pose().multiply(pose); + return this; + } + + @Override + public TransformStack mulNormal(Matrix3f normal) { + internal.last().normal().mul(normal); + return this; + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/transform/TransformStack.java b/src/main/java/com/jozufozu/flywheel/util/transform/TransformStack.java index da521b69b..453953a9b 100644 --- a/src/main/java/com/jozufozu/flywheel/util/transform/TransformStack.java +++ b/src/main/java/com/jozufozu/flywheel/util/transform/TransformStack.java @@ -2,7 +2,7 @@ package com.jozufozu.flywheel.util.transform; import com.mojang.blaze3d.vertex.PoseStack; -public interface TransformStack extends Scale, Translate, Rotate, TStack { +public interface TransformStack extends Transform, TStack { static TransformStack cast(PoseStack stack) { return (TransformStack) stack; } From 7c1de5c06a24b56fdcbff7e7b97a3c150c2f03c8 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 22 Dec 2021 22:29:52 -0800 Subject: [PATCH 16/22] Small config changes - Make FlwCommands more readable - EmptyArgumentSerializer for EngineArgument --- .../java/com/jozufozu/flywheel/Flywheel.java | 3 +- .../flywheel/config/EngineArgument.java | 23 +++---------- .../flywheel/config/EngineConfigCommand.java | 32 ------------------- .../jozufozu/flywheel/config/FlwCommands.java | 32 +++++++++++++++++-- 4 files changed, 36 insertions(+), 54 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index 956a6192b..aeb110dc4 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -9,6 +9,7 @@ import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwPackets; import net.minecraft.commands.synchronization.ArgumentTypes; +import net.minecraft.commands.synchronization.EmptyArgumentSerializer; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; @@ -41,6 +42,6 @@ public class Flywheel { private void setup(final FMLCommonSetupEvent event) { FlwPackets.registerPackets(); - ArgumentTypes.register("flywheel:engine", EngineArgument.class, EngineArgument.SERIALIZER); + ArgumentTypes.register("flywheel:engine", EngineArgument.class, new EmptyArgumentSerializer<>(EngineArgument::getInstance)); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/EngineArgument.java b/src/main/java/com/jozufozu/flywheel/config/EngineArgument.java index f24a758cb..90588554f 100644 --- a/src/main/java/com/jozufozu/flywheel/config/EngineArgument.java +++ b/src/main/java/com/jozufozu/flywheel/config/EngineArgument.java @@ -2,9 +2,7 @@ package com.jozufozu.flywheel.config; import java.util.Collection; import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; -import com.google.gson.JsonObject; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.context.CommandContext; @@ -14,16 +12,14 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.commands.synchronization.ArgumentSerializer; -import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.TranslatableComponent; public class EngineArgument implements ArgumentType { - public static final EngineArgument INSTANCE = new EngineArgument(); - public static final Serializer SERIALIZER = new Serializer(); + private static final EngineArgument INSTANCE = new EngineArgument(); private static final Dynamic2CommandExceptionType INVALID = new Dynamic2CommandExceptionType((found, constants) -> { + // TODO: don't steal lang return new TranslatableComponent("commands.forge.arguments.enum.invalid", constants, found); }); @@ -52,18 +48,7 @@ public class EngineArgument implements ArgumentType { return FlwEngine.validNames(); } - public static class Serializer implements ArgumentSerializer { - private Serializer() { - } - - public void serializeToNetwork(EngineArgument argument, FriendlyByteBuf buffer) { - } - - public EngineArgument deserializeFromNetwork(FriendlyByteBuf buffer) { - return INSTANCE; - } - - public void serializeToJson(EngineArgument argument, JsonObject json) { - } + public static EngineArgument getInstance() { + return INSTANCE; } } diff --git a/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java b/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java deleted file mode 100644 index e8aefc36f..000000000 --- a/src/main/java/com/jozufozu/flywheel/config/EngineConfigCommand.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jozufozu.flywheel.config; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.builder.ArgumentBuilder; - -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.network.PacketDistributor; -import net.minecraftforge.server.command.EnumArgument; - -public class EngineConfigCommand { - public static ArgumentBuilder register() { - return Commands.literal("backend") - .executes(context -> { - ServerPlayer player = context.getSource() - .getPlayerOrException(); - FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket()); - return Command.SINGLE_SUCCESS; - }) - .then(Commands.argument("type", EngineArgument.INSTANCE) - .executes(context -> { - FlwEngine type = context.getArgument("type", FlwEngine.class); - - ServerPlayer player = context.getSource() - .getPlayerOrException(); - FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket(type)); - - return Command.SINGLE_SUCCESS; - })); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index af8f9648b..6778d9996 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -1,11 +1,15 @@ package com.jozufozu.flywheel.config; +import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.ArgumentBuilder; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.event.server.ServerStartingEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.network.PacketDistributor; public class FlwCommands { @SubscribeEvent @@ -15,8 +19,32 @@ public class FlwCommands { .getDispatcher(); dispatcher.register(Commands.literal("flywheel") - .then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register()) - .then(EngineConfigCommand.register()) + .then(debugCommand()) + .then(backendCommand()) ); } + + private static ArgumentBuilder debugCommand() { + return new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register(); + } + + private static ArgumentBuilder backendCommand() { + return Commands.literal("backend") + .executes(context -> { + ServerPlayer player = context.getSource() + .getPlayerOrException(); + FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket()); + return Command.SINGLE_SUCCESS; + }) + .then(Commands.argument("type", EngineArgument.getInstance()) + .executes(context -> { + FlwEngine type = context.getArgument("type", FlwEngine.class); + + ServerPlayer player = context.getSource() + .getPlayerOrException(); + FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket(type)); + + return Command.SINGLE_SUCCESS; + })); + } } From 5cca71332d21041e1fb1c6e9d46ac25630496d5a Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 22 Dec 2021 22:30:48 -0800 Subject: [PATCH 17/22] Bump version - 0.5.0 --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + gradle.properties | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index b4f0908b7..a48b1b1a6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -59,6 +59,7 @@ body: label: Mod Version description: The version of the mod you were using when the bug occured options: + - "0.5.0" - "0.4.2-rc" - "0.4.1" - "0.4.0" diff --git a/gradle.properties b/gradle.properties index 5f7161780..686c3f09c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs = -Xmx3G org.gradle.daemon = false # mod version info -mod_version = 0.4.2-rc +mod_version = 0.5.0 mc_update_version = 1.18 minecraft_version = 1.18.1 forge_version = 39.0.5 From 082fe8cdd8e76a681c2aeb17a81ba9466189a24e Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 23 Dec 2021 23:09:51 -0800 Subject: [PATCH 18/22] Switch back to parchment, update forge --- build.gradle | 2 +- gradle.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index eeda96d6d..f3a3818e2 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ java.toolchain.languageVersion = JavaLanguageVersion.of(17) println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) minecraft { - mappings channel: 'official', version: "${minecraft_version}" // TODO: waiting for parchment 1.18 + mappings channel: 'parchment', version: "${parchment_version}-${minecraft_version}" runs { client { diff --git a/gradle.properties b/gradle.properties index 5f7161780..6ca80de60 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.daemon = false mod_version = 0.4.2-rc mc_update_version = 1.18 minecraft_version = 1.18.1 -forge_version = 39.0.5 +forge_version = 39.0.8 # build dependency versions forgegradle_version = 5.1.+ @@ -13,7 +13,7 @@ mixingradle_version = 0.7-SNAPSHOT mixin_version = 0.8.5 librarian_version = 1.+ cursegradle_version = 1.4.0 -parchment_version = 2021.10.31 +parchment_version = 2021.12.19 # curseforge info projectId = 486392 From 4d5391f5dcad5edcc4b5aad4738035e7d034de5a Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 23 Dec 2021 14:41:10 -0800 Subject: [PATCH 19/22] Contraptions and engines - Sort of get the batching engine working for contraptions but this feels wrong - TaskEngine gets passed in methods - Better naming for TaskEngines - Do BufferSource ourselves - Change BufferBuilderMixin to allow for injection into BufferBuilder objects --- .../backend/instancing/AbstractInstancer.java | 5 ++ .../backend/instancing/InstanceManager.java | 8 +- .../backend/instancing/InstanceWorld.java | 34 ++++---- .../instancing/InstancedRenderDispatcher.java | 2 +- ...hExecutor.java => ParallelTaskEngine.java} | 21 ++--- .../backend/instancing/RenderDispatcher.java | 6 +- ...ateExecutor.java => SerialTaskEngine.java} | 6 +- .../backend/instancing/SuperBufferSource.java | 79 +++++++++++++++++++ .../batching/BatchedMaterialGroup.java | 23 +----- .../instancing/batching/BatchingEngine.java | 40 +++------- .../entity/EntityInstanceManager.java | 5 +- .../instancing/InstancingEngine.java | 16 +--- .../instancing/tile/TileInstanceManager.java | 5 +- .../backend/model/BufferBuilderHack.java | 16 ++++ .../backend/model/DirectBufferBuilder.java | 20 ----- .../backend/model/DirectVertexConsumer.java | 2 +- .../crumbling/CrumblingInstanceManager.java | 3 +- .../core/crumbling/CrumblingRenderer.java | 5 +- .../flywheel/mixin/BufferBuilderMixin.java | 51 ++++++------ 19 files changed, 197 insertions(+), 150 deletions(-) rename src/main/java/com/jozufozu/flywheel/backend/instancing/{BatchExecutor.java => ParallelTaskEngine.java} (87%) rename src/main/java/com/jozufozu/flywheel/backend/instancing/{ImmediateExecutor.java => SerialTaskEngine.java} (59%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/SuperBufferSource.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/BufferBuilderHack.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index 3a8731f71..ef1a6824c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -115,4 +115,9 @@ public abstract class AbstractInstancer implements Insta return instanceData; } + + @Override + public String toString() { + return "Instancer[" + modelData + ']'; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 449c04862..6741c513c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -32,13 +32,11 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift protected final Map instances; protected final Object2ObjectOpenHashMap tickableInstances; protected final Object2ObjectOpenHashMap dynamicInstances; - private final TaskEngine taskEngine; protected int frame; protected int tick; - public InstanceManager(TaskEngine taskEngine, MaterialManager materialManager) { - this.taskEngine = taskEngine; + public InstanceManager(MaterialManager materialManager) { this.materialManager = materialManager; this.queuedUpdates = new HashSet<>(64); this.queuedAdditions = new HashSet<>(64); @@ -78,7 +76,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift * Queued updates are processed. *

*/ - public void tick(double cameraX, double cameraY, double cameraZ) { + public void tick(TaskEngine taskEngine, double cameraX, double cameraY, double cameraZ) { tick++; processQueuedUpdates(); @@ -120,7 +118,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick(); } - public void beginFrame(Camera info) { + public void beginFrame(TaskEngine taskEngine, Camera info) { frame++; processQueuedAdditions(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index a913b442d..a6167ab24 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -16,6 +16,8 @@ import com.jozufozu.flywheel.event.RenderLayerEvent; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.entity.BlockEntity; /** @@ -29,11 +31,12 @@ public class InstanceWorld { protected final InstanceManager entityInstanceManager; protected final InstanceManager tileEntityInstanceManager; - protected final BatchExecutor taskEngine; + protected final ParallelTaskEngine taskEngine; - public InstanceWorld() { + public InstanceWorld(LevelAccessor levelAccessor) { + Level world = (Level) levelAccessor; - this.taskEngine = new BatchExecutor(); + this.taskEngine = new ParallelTaskEngine("Flywheel " + world.dimension().location()); this.taskEngine.startWorkers(); FlwEngine engine = Backend.getInstance() @@ -42,19 +45,19 @@ public class InstanceWorld { switch (engine) { case INSTANCING -> { InstancingEngine manager = InstancingEngine.builder(Contexts.WORLD) - .build(this.taskEngine); + .build(); - entityInstanceManager = new EntityInstanceManager(this.taskEngine, manager); - tileEntityInstanceManager = new TileInstanceManager(this.taskEngine, manager); + entityInstanceManager = new EntityInstanceManager(manager); + tileEntityInstanceManager = new TileInstanceManager(manager); manager.addListener(entityInstanceManager); manager.addListener(tileEntityInstanceManager); this.engine = manager; } case BATCHING -> { - this.engine = new BatchingEngine(this.taskEngine); - entityInstanceManager = new EntityInstanceManager(this.taskEngine, this.engine); - tileEntityInstanceManager = new TileInstanceManager(this.taskEngine, this.engine); + this.engine = new BatchingEngine(); + entityInstanceManager = new EntityInstanceManager(this.engine); + tileEntityInstanceManager = new TileInstanceManager(this.engine); } default -> throw new IllegalArgumentException("Unknown engine type"); } @@ -91,8 +94,8 @@ public class InstanceWorld { taskEngine.syncPoint(); - tileEntityInstanceManager.beginFrame(event.getInfo()); - entityInstanceManager.beginFrame(event.getInfo()); + tileEntityInstanceManager.beginFrame(taskEngine, event.getInfo()); + entityInstanceManager.beginFrame(taskEngine, event.getInfo()); } /** @@ -107,8 +110,8 @@ public class InstanceWorld { if (renderViewEntity == null) return; - tileEntityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); - entityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); + tileEntityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); + entityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); } /** @@ -116,7 +119,10 @@ public class InstanceWorld { */ public void renderLayer(RenderLayerEvent event) { taskEngine.syncPoint(); - engine.render(event); + event.stack.pushPose(); + event.stack.translate(-event.camX, -event.camY, -event.camZ); + engine.render(taskEngine, event); + event.stack.popPose(); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index 931908b3f..216cceebc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -22,7 +22,7 @@ import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(Dist.CLIENT) public class InstancedRenderDispatcher { - private static final WorldAttached instanceWorlds = new WorldAttached<>($ -> new InstanceWorld()); + private static final WorldAttached instanceWorlds = new WorldAttached<>(InstanceWorld::new); /** * Call this when you want to manually run {@link AbstractInstance#update()}. diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/ParallelTaskEngine.java similarity index 87% rename from src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java rename to src/main/java/com/jozufozu/flywheel/backend/instancing/ParallelTaskEngine.java index da856e57c..937567853 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/BatchExecutor.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/ParallelTaskEngine.java @@ -17,7 +17,7 @@ import com.jozufozu.flywheel.backend.instancing.batching.WaitGroup; import net.minecraft.util.Mth; // https://github.com/CaffeineMC/sodium-fabric/blob/5d364ed5ba63f9067fcf72a078ca310bff4db3e9/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuilder.java -public class BatchExecutor implements TaskEngine { +public class ParallelTaskEngine implements TaskEngine { private static final Logger LOGGER = LogManager.getLogger("BatchExecutor"); private final AtomicBoolean running = new AtomicBoolean(false); @@ -30,7 +30,10 @@ public class BatchExecutor implements TaskEngine { private final int threadCount; - public BatchExecutor() { + private final String name; + + public ParallelTaskEngine(String name) { + this.name = name; threadCount = getOptimalThreadCount(); } @@ -49,7 +52,7 @@ public class BatchExecutor implements TaskEngine { for (int i = 0; i < this.threadCount; i++) { - Thread thread = new Thread(new WorkerRunnable(), "Engine Executor " + i); + Thread thread = new Thread(new WorkerRunnable(), name + " " + i); thread.setPriority(Math.max(0, Thread.NORM_PRIORITY - 2)); thread.start(); @@ -121,9 +124,9 @@ public class BatchExecutor implements TaskEngine { Runnable job = this.jobQueue.pollFirst(); if (job == null) { - synchronized (BatchExecutor.this.jobNotifier) { + synchronized (ParallelTaskEngine.this.jobNotifier) { try { - BatchExecutor.this.jobNotifier.wait(); + ParallelTaskEngine.this.jobNotifier.wait(); } catch (InterruptedException ignored) { } } @@ -138,7 +141,7 @@ public class BatchExecutor implements TaskEngine { } catch (Exception e) { Flywheel.log.error(e); } finally { - BatchExecutor.this.wg.done(); + ParallelTaskEngine.this.wg.done(); } } @@ -155,19 +158,19 @@ public class BatchExecutor implements TaskEngine { } private class WorkerRunnable implements Runnable { - private final AtomicBoolean running = BatchExecutor.this.running; + private final AtomicBoolean running = ParallelTaskEngine.this.running; @Override public void run() { // Run until the chunk builder shuts down while (this.running.get()) { - Runnable job = BatchExecutor.this.getNextTask(); + Runnable job = ParallelTaskEngine.this.getNextTask(); if (job == null) { continue; } - BatchExecutor.this.processTask(job); + ParallelTaskEngine.this.processTask(job); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java index ae9ccf88f..4d5cf8372 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java @@ -3,15 +3,15 @@ package com.jozufozu.flywheel.backend.instancing; import com.jozufozu.flywheel.event.RenderLayerEvent; import net.minecraft.client.Camera; -import net.minecraft.client.renderer.MultiBufferSource; public interface RenderDispatcher { /** * Render every model for every material. * - * @param event Context for rendering. + * @param taskEngine + * @param event Context for rendering. */ - void render(RenderLayerEvent event); + void render(TaskEngine taskEngine, RenderLayerEvent event); /** * Maintain the integer origin coordinate to be within a certain distance from the camera in all directions. diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/ImmediateExecutor.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/SerialTaskEngine.java similarity index 59% rename from src/main/java/com/jozufozu/flywheel/backend/instancing/ImmediateExecutor.java rename to src/main/java/com/jozufozu/flywheel/backend/instancing/SerialTaskEngine.java index be1c2b5fd..96361a2d6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/ImmediateExecutor.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/SerialTaskEngine.java @@ -2,11 +2,11 @@ package com.jozufozu.flywheel.backend.instancing; import org.jetbrains.annotations.NotNull; -public class ImmediateExecutor implements TaskEngine { +public class SerialTaskEngine implements TaskEngine { - public static final ImmediateExecutor INSTANCE = new ImmediateExecutor(); + public static final SerialTaskEngine INSTANCE = new SerialTaskEngine(); - private ImmediateExecutor() { + private SerialTaskEngine() { } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/SuperBufferSource.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/SuperBufferSource.java new file mode 100644 index 000000000..d4b8636f5 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/SuperBufferSource.java @@ -0,0 +1,79 @@ +package com.jozufozu.flywheel.backend.instancing; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import com.jozufozu.flywheel.backend.model.BufferBuilderHack; +import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; +import com.mojang.blaze3d.platform.MemoryTracker; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.VertexFormat; + +import net.minecraft.client.renderer.RenderType; + +public class SuperBufferSource { + + protected final Map buffers = new HashMap<>(); + private final BufferBuilder scratch; + + public SuperBufferSource() { + scratch = new BufferBuilder(8); + + ((BufferBuilderHack) scratch).freeBuffer(); + } + + public DirectVertexConsumer getBuffer(RenderType renderType, int vertexCount) { + return buffers.computeIfAbsent(renderType, DrawBuffer::new) + .begin(vertexCount); + } + + public void endBatch() { + // TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders + // into the RenderBuffers from context. + + BufferBuilderHack hack = (BufferBuilderHack) scratch; + + for (Map.Entry entry : buffers.entrySet()) { + DrawBuffer builder = entry.getValue(); + + if (builder.expectedVertices > 0) { + RenderType type = entry.getKey(); + + hack.hackBegin(builder.backingBuffer, type.format(), builder.expectedVertices); + + type.end(scratch, 0, 0, 0); + + builder.expectedVertices = 0; + } + } + } + + private static class DrawBuffer { + + private final RenderType type; + private ByteBuffer backingBuffer; + private int expectedVertices; + + public DrawBuffer(RenderType type) { + this.type = type; + } + + public DirectVertexConsumer begin(int vertexCount) { + this.expectedVertices = vertexCount; + + VertexFormat format = type.format(); + + int byteSize = format.getVertexSize() * vertexCount; + + if (backingBuffer == null) { + backingBuffer = MemoryTracker.create(byteSize); + } if (byteSize > backingBuffer.capacity()) { + backingBuffer = MemoryTracker.resize(backingBuffer, byteSize); + } + + return new DirectVertexConsumer(backingBuffer, format, vertexCount); + } + + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java index ba5d71105..cbeaf7c60 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java @@ -7,13 +7,11 @@ import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.instancing.SuperBufferSource; import com.jozufozu.flywheel.backend.instancing.TaskEngine; -import com.jozufozu.flywheel.backend.model.DirectBufferBuilder; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; -import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; public class BatchedMaterialGroup implements MaterialGroup { @@ -36,17 +34,8 @@ public class BatchedMaterialGroup implements MaterialGroup { } } - public void render(PoseStack stack, MultiBufferSource source, TaskEngine pool) { - VertexConsumer buffer = source.getBuffer(state); + public void render(PoseStack stack, SuperBufferSource source, TaskEngine pool) { - if (buffer instanceof DirectBufferBuilder direct) { - renderParallel(stack, pool, direct); - } else { - renderSerial(stack, buffer); - } - } - - private void renderParallel(PoseStack stack, TaskEngine pool, DirectBufferBuilder direct) { int vertexCount = 0; for (BatchedMaterial material : materials.values()) { for (CPUInstancer instancer : material.models.values()) { @@ -55,7 +44,7 @@ public class BatchedMaterialGroup implements MaterialGroup { } } - DirectVertexConsumer consumer = direct.intoDirectConsumer(vertexCount); + DirectVertexConsumer consumer = source.getBuffer(state, vertexCount); // avoids rendering garbage, but doesn't fix the issue of some instances not being buffered consumer.memSetZero(); @@ -74,12 +63,6 @@ public class BatchedMaterialGroup implements MaterialGroup { } } - private void renderSerial(PoseStack stack, VertexConsumer consumer) { - for (BatchedMaterial value : materials.values()) { - value.setupAndRenderInto(stack, consumer); - } - } - public void clear() { materials.values().forEach(BatchedMaterial::clear); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index c8ebbed58..caec4b844 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -6,6 +6,7 @@ import java.util.Map; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.backend.RenderLayer; +import com.jozufozu.flywheel.backend.instancing.SuperBufferSource; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.event.RenderLayerEvent; @@ -21,19 +22,17 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; -public class BatchingEngine implements Engine, MultiBufferSource { +public class BatchingEngine implements Engine { - protected final Map buffers = new HashMap<>(); - protected final Map> layers; - protected final TaskEngine taskEngine; + private final Map> layers; + private final SuperBufferSource superBufferSource = new SuperBufferSource(); - public BatchingEngine(TaskEngine taskEngine) { + public BatchingEngine() { this.layers = new EnumMap<>(RenderLayer.class); for (RenderLayer value : RenderLayer.values()) { layers.put(value, new HashMap<>()); } - this.taskEngine = taskEngine; } @Override @@ -47,21 +46,15 @@ public class BatchingEngine implements Engine, MultiBufferSource { } @Override - public void render(RenderLayerEvent event) { - PoseStack stack = event.stack; + public void render(TaskEngine taskEngine, RenderLayerEvent event) { - stack.pushPose(); - - stack.translate(-event.camX, -event.camY, -event.camZ); - - for (BatchedMaterialGroup group : layers.get(event.getLayer()).values()) { - group.render(stack, this, taskEngine); + Map groups = layers.get(event.getLayer()); + for (BatchedMaterialGroup group : groups.values()) { + group.render(event.stack, superBufferSource, taskEngine); } taskEngine.syncPoint(); - stack.popPose(); - // FIXME: this probably breaks some vanilla stuff but it works much better for flywheel Matrix4f mat = new Matrix4f(); mat.setIdentity(); @@ -71,20 +64,7 @@ public class BatchingEngine implements Engine, MultiBufferSource { Lighting.setupLevel(mat); } - // TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders - // into the RenderBuffers from context. - buffers.forEach((type, builder) -> type.end(builder, 0, 0, 0)); - } - - @Override - public VertexConsumer getBuffer(RenderType renderType) { - BufferBuilder builder = buffers.computeIfAbsent(renderType, type -> new BufferBuilder(type.bufferSize())); - - if (!builder.building()) { - builder.begin(renderType.mode(), renderType.format()); - } - - return builder; + superBufferSource.endBatch(); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstanceManager.java index 2b3af7808..494800ae2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstanceManager.java @@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.entity; import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.AbstractInstance; -import com.jozufozu.flywheel.backend.instancing.BatchExecutor; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; import com.jozufozu.flywheel.backend.instancing.TaskEngine; @@ -15,8 +14,8 @@ import net.minecraft.world.level.Level; public class EntityInstanceManager extends InstanceManager { - public EntityInstanceManager(TaskEngine executor, MaterialManager materialManager) { - super(executor, materialManager); + public EntityInstanceManager(MaterialManager materialManager) { + super(materialManager); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 63015b349..7f30b541e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -10,7 +10,6 @@ import javax.annotation.Nullable; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.backend.instancing.TaskEngine; -import com.jozufozu.flywheel.backend.instancing.ImmediateExecutor; import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; @@ -22,7 +21,6 @@ import com.jozufozu.flywheel.util.WeakHashSet; import com.mojang.math.Matrix4f; import net.minecraft.client.Camera; -import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; @@ -35,7 +33,6 @@ public class InstancingEngine

implements Engine { protected BlockPos originCoordinate = BlockPos.ZERO; - protected final TaskEngine taskEngine; protected final WorldContext

context; protected final GroupFactory

groupFactory; protected final boolean ignoreOriginCoordinate; @@ -45,15 +42,14 @@ public class InstancingEngine

implements Engine { private final WeakHashSet listeners; public InstancingEngine(WorldContext

context, TaskEngine taskEngine) { - this(taskEngine, context, InstancedMaterialGroup::new, false); + this(context, InstancedMaterialGroup::new, false); } public static

Builder

builder(WorldContext

context) { return new Builder<>(context); } - public InstancingEngine(TaskEngine taskEngine, WorldContext

context, GroupFactory

groupFactory, boolean ignoreOriginCoordinate) { - this.taskEngine = taskEngine; + public InstancingEngine(WorldContext

context, GroupFactory

groupFactory, boolean ignoreOriginCoordinate) { this.context = context; this.ignoreOriginCoordinate = ignoreOriginCoordinate; @@ -82,7 +78,7 @@ public class InstancingEngine

implements Engine { * Render every model for every material. */ @Override - public void render(RenderLayerEvent event) { + public void render(TaskEngine taskEngine, RenderLayerEvent event) { double camX; double camY; double camZ; @@ -205,11 +201,7 @@ public class InstancingEngine

implements Engine { } public InstancingEngine

build() { - return build(ImmediateExecutor.INSTANCE); - } - - public InstancingEngine

build(TaskEngine taskEngine) { - return new InstancingEngine<>(taskEngine, context, groupFactory, ignoreOriginCoordinate); + return new InstancingEngine<>(context, groupFactory, ignoreOriginCoordinate); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileInstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileInstanceManager.java index c0b65215d..b0be524da 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileInstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileInstanceManager.java @@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.tile; import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.AbstractInstance; -import com.jozufozu.flywheel.backend.instancing.BatchExecutor; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; import com.jozufozu.flywheel.backend.instancing.TaskEngine; @@ -15,8 +14,8 @@ import net.minecraft.world.level.block.entity.BlockEntity; public class TileInstanceManager extends InstanceManager { - public TileInstanceManager(TaskEngine executor, MaterialManager materialManager) { - super(executor, materialManager); + public TileInstanceManager(MaterialManager materialManager) { + super(materialManager); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferBuilderHack.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferBuilderHack.java new file mode 100644 index 000000000..ee2dd4fb8 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferBuilderHack.java @@ -0,0 +1,16 @@ +package com.jozufozu.flywheel.backend.model; + +import java.nio.ByteBuffer; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.VertexFormat; + +/** + * Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory. + */ +public interface BufferBuilderHack { + + void freeBuffer(); + + void hackBegin(ByteBuffer buffer, VertexFormat format, int vertexCount); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java deleted file mode 100644 index c157aed9b..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/model/DirectBufferBuilder.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jozufozu.flywheel.backend.model; - -import com.mojang.blaze3d.vertex.BufferBuilder; - -/** - * Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory. - */ -public interface DirectBufferBuilder { - - /** - * Create a DirectVertexConsumer from this BufferBuilder. - * - *

- * After this function returns, the internal state of the BufferBuilder will be as if - * {@link BufferBuilder#endVertex()} was called vertexCount times. It is up to the callee - * to actually populate the BufferBuilder with vertices using the returned value. - *

- */ - DirectVertexConsumer intoDirectConsumer(int vertexCount); -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java index 781ad8693..8446fbfcc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/DirectVertexConsumer.java @@ -13,7 +13,7 @@ import com.mojang.blaze3d.vertex.VertexFormatElement; /** * An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer. * - * @see DirectBufferBuilder + * @see BufferBuilderHack */ public class DirectVertexConsumer implements VertexConsumer { public final VertexFormat format; diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java index bea324fd4..30e427f6b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java @@ -1,7 +1,6 @@ package com.jozufozu.flywheel.core.crumbling; import com.jozufozu.flywheel.api.MaterialManager; -import com.jozufozu.flywheel.backend.instancing.ImmediateExecutor; import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; import net.minecraft.core.BlockPos; @@ -9,7 +8,7 @@ import net.minecraft.core.BlockPos; public class CrumblingInstanceManager extends TileInstanceManager { public CrumblingInstanceManager(MaterialManager materialManager) { - super(ImmediateExecutor.INSTANCE, materialManager); + super(materialManager); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index 42ae86e71..4aad9a96b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -6,6 +6,7 @@ import java.util.SortedSet; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlTextureUnit; +import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.core.Contexts; @@ -74,9 +75,9 @@ public class CrumblingRenderer { if (_currentLayer != null) { stage.getValue().forEach(instanceManager::add); - instanceManager.beginFrame(info); + instanceManager.beginFrame(SerialTaskEngine.INSTANCE, info); - materials.render(event); + materials.render(SerialTaskEngine.INSTANCE, event); instanceManager.invalidate(); } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java index 66c29bfd4..120f552c1 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java @@ -5,29 +5,34 @@ import java.nio.ByteBuffer; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.lwjgl.system.MemoryUtil; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import com.jozufozu.flywheel.backend.instancing.SuperBufferSource; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; -import com.jozufozu.flywheel.backend.model.DirectBufferBuilder; +import com.jozufozu.flywheel.backend.model.BufferBuilderHack; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormatElement; @Mixin(BufferBuilder.class) -public abstract class BufferBuilderMixin implements DirectBufferBuilder { +public abstract class BufferBuilderMixin implements BufferBuilderHack { @Shadow private ByteBuffer buffer; + @Shadow + private boolean building; + + @Shadow + public abstract void begin(VertexFormat.Mode p_166780_, VertexFormat p_166781_); + + @Shadow + private VertexFormat.Mode mode; + @Shadow private VertexFormat format; - @Shadow - protected abstract void ensureCapacity(int p_85723_); - - @Shadow - private int vertices; - @Shadow @Nullable private VertexFormatElement currentElement; @@ -36,24 +41,26 @@ public abstract class BufferBuilderMixin implements DirectBufferBuilder { private int elementIndex; @Shadow - private int nextElementByte; + private int vertices; @Override - @Nonnull - public DirectVertexConsumer intoDirectConsumer(int vertexCount) { - int bytes = vertexCount * format.getVertexSize(); - // ensure we have capacity for one extra vertex, BufferBuilder does this on #endVertex - ensureCapacity(bytes + format.getVertexSize()); + public void freeBuffer() { + if (this.buffer != null) { + MemoryUtil.memFree(this.buffer); + this.buffer = null; + } + } - DirectVertexConsumer consumer = new DirectVertexConsumer(this.buffer, this.format, vertexCount); + @Override + public void hackBegin(@Nonnull ByteBuffer buffer, @Nonnull VertexFormat format, int vertexCount) { + this.building = true; + this.mode = VertexFormat.Mode.QUADS; - this.vertices += vertexCount; - this.currentElement = format.getElements() - .get(0); + this.buffer = buffer; + this.format = format; + this.vertices = vertexCount; + + this.currentElement = this.format.getElements().get(0); this.elementIndex = 0; - this.nextElementByte += bytes; - this.buffer.position(this.buffer.position() + bytes); - - return consumer; } } From f4cdbb73f79879a70165b65643e0b786da9879ce Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 24 Dec 2021 01:45:38 -0800 Subject: [PATCH 20/22] Fix GL error spam and do some cleanup - Add flywheel$ prefix to mixin duck interfaces/accessors - Better chat messages for /flywheel backend command - Track VAO via vanilla --- .../flywheel/backend/gl/GlVertexArray.java | 6 ++- .../flywheel/backend/gl/shader/GlProgram.java | 4 +- .../backend/instancing/SuperBufferSource.java | 4 +- .../backend/model/BufferBuilderHack.java | 4 +- .../jozufozu/flywheel/config/FlwEngine.java | 38 +++++++++++++++---- .../flywheel/core/crumbling/AtlasInfo.java | 2 +- .../core/crumbling/CrumblingRenderer.java | 2 +- .../materials/model/ModelWriterUnsafe.java | 6 +-- .../flywheel/event/RenderLayerEvent.java | 2 +- .../flywheel/mixin/BufferBuilderMixin.java | 4 +- .../mixin/BufferUploaderAccessor.java | 16 ++++++++ .../flywheel/mixin/LevelRendererAccessor.java | 4 +- .../mixin/ShaderInstanceAccessor.java | 2 +- .../mixin/atlas/SheetDataAccessor.java | 4 +- .../flywheel/mixin/matrix/Matrix3fMixin.java | 9 ++--- .../flywheel/mixin/matrix/Matrix4fMixin.java | 9 ++--- .../jozufozu/flywheel/util/MatrixWrite.java | 17 +++++++++ .../com/jozufozu/flywheel/util/WriteSafe.java | 7 ---- .../jozufozu/flywheel/util/WriteUnsafe.java | 9 ----- src/main/resources/flywheel.mixins.json | 1 + 20 files changed, 96 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/mixin/BufferUploaderAccessor.java create mode 100644 src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java delete mode 100644 src/main/java/com/jozufozu/flywheel/util/WriteSafe.java delete mode 100644 src/main/java/com/jozufozu/flywheel/util/WriteUnsafe.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java index c75e1e117..18f7bfc26 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.backend.gl; +import com.jozufozu.flywheel.mixin.BufferUploaderAccessor; import com.jozufozu.flywheel.util.AttribUtil; import com.mojang.blaze3d.platform.GlStateManager; @@ -9,11 +10,14 @@ public class GlVertexArray extends GlObject { } public void bind() { - GlStateManager._glBindVertexArray(handle()); + int handle = handle(); + GlStateManager._glBindVertexArray(handle); + BufferUploaderAccessor.flywheel$setLastVAO(handle); } public static void unbind() { GlStateManager._glBindVertexArray(0); + BufferUploaderAccessor.flywheel$setLastVAO(0); } protected void deleteInternal(int handle) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java index 2536ba21b..8fbac95cd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java @@ -31,12 +31,12 @@ public abstract class GlProgram extends GlObject { public void bind() { int handle = handle(); ProgramManager.glUseProgram(handle); - ShaderInstanceAccessor.setLastProgramId(handle); + ShaderInstanceAccessor.flywheel$setLastProgramId(handle); } public void unbind() { ProgramManager.glUseProgram(0); - ShaderInstanceAccessor.setLastProgramId(0); + ShaderInstanceAccessor.flywheel$setLastProgramId(0); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/SuperBufferSource.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/SuperBufferSource.java index d4b8636f5..20984c03b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/SuperBufferSource.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/SuperBufferSource.java @@ -20,7 +20,7 @@ public class SuperBufferSource { public SuperBufferSource() { scratch = new BufferBuilder(8); - ((BufferBuilderHack) scratch).freeBuffer(); + ((BufferBuilderHack) scratch).flywheel$freeBuffer(); } public DirectVertexConsumer getBuffer(RenderType renderType, int vertexCount) { @@ -40,7 +40,7 @@ public class SuperBufferSource { if (builder.expectedVertices > 0) { RenderType type = entry.getKey(); - hack.hackBegin(builder.backingBuffer, type.format(), builder.expectedVertices); + hack.flywheel$hackBegin(builder.backingBuffer, type.format(), builder.expectedVertices); type.end(scratch, 0, 0, 0); diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferBuilderHack.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferBuilderHack.java index ee2dd4fb8..2fc98cf21 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferBuilderHack.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferBuilderHack.java @@ -10,7 +10,7 @@ import com.mojang.blaze3d.vertex.VertexFormat; */ public interface BufferBuilderHack { - void freeBuffer(); + void flywheel$freeBuffer(); - void hackBegin(ByteBuffer buffer, VertexFormat format, int vertexCount); + void flywheel$hackBegin(ByteBuffer buffer, VertexFormat format, int vertexCount); } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java b/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java index 2692c63e1..f0a977224 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwEngine.java @@ -6,6 +6,8 @@ import java.util.Map; import javax.annotation.Nullable; +import org.jetbrains.annotations.NotNull; + import com.jozufozu.flywheel.backend.Backend; import net.minecraft.ChatFormatting; @@ -13,12 +15,14 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextComponent; +import net.minecraftforge.fml.ModList; public enum FlwEngine { - OFF("off", "Off", new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED)), - BATCHING("batching", "Parallel Batching", new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN)), - INSTANCING("instancing", "GL33 Instanced Arrays", new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN)), + OFF("off", "Off"), + BATCHING("batching", "Parallel Batching"), + INSTANCING("instancing", "GL33 Instanced Arrays"), ; private static final Map lookup; @@ -30,14 +34,12 @@ public enum FlwEngine { } } - private final Component message; private final String shortName; private final String properName; - FlwEngine(String shortName, String properName, Component message) { + FlwEngine(String shortName, String properName) { this.shortName = shortName; this.properName = properName; - this.message = message; } public String getProperName() { @@ -55,13 +57,33 @@ public enum FlwEngine { if (type != null) { FlwConfig.get().client.engine.set(type); - player.displayClientMessage(type.message, false); + Component message = getMessage(type); + + player.displayClientMessage(message, false); Backend.reloadWorldRenderers(); } else { - player.displayClientMessage(FlwConfig.get().getEngine().message, false); + player.displayClientMessage(getMessage(FlwConfig.get().getEngine()), false); } } + private static Component getMessage(@NotNull FlwEngine type) { + return switch (type) { + case OFF -> new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED); + case INSTANCING -> new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN); + case BATCHING -> { + MutableComponent msg = new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN); + + if (ModList.get() + .isLoaded("create")) { + // FIXME: batching engine contraption lighting issues + msg.append(new TextComponent("\nWARNING: May cause issues with Create Contraptions").withStyle(ChatFormatting.RED)); + } + + yield msg; + } + }; + } + @Nullable public static FlwEngine decode(FriendlyByteBuf buffer) { byte b = buffer.readByte(); diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java index a21ba81b8..1289a3db9 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java @@ -40,7 +40,7 @@ public class AtlasInfo { * FOR USE IN MIXIN */ public static void _setAtlasData(ResourceLocation atlas, SheetDataAccessor accessor) { - sheetData.put(atlas, new SheetSize(accessor.getWidth(), accessor.getHeight())); + sheetData.put(atlas, new SheetSize(accessor.flywheel$getWidth(), accessor.flywheel$getHeight())); } public record SheetSize(int width, int height) { diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index 4aad9a96b..056790864 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -96,7 +96,7 @@ public class CrumblingRenderer { Int2ObjectMap> breakingEntities = new Int2ObjectArrayMap<>(); - for (Long2ObjectMap.Entry> entry : ((LevelRendererAccessor) Minecraft.getInstance().levelRenderer).getDestructionProgress() + for (Long2ObjectMap.Entry> entry : ((LevelRendererAccessor) Minecraft.getInstance().levelRenderer).flywheel$getDestructionProgress() .long2ObjectEntrySet()) { BlockPos breakingPos = BlockPos.of(entry.getLongKey()); diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java index 6fac04507..354751f4e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelWriterUnsafe.java @@ -3,7 +3,7 @@ package com.jozufozu.flywheel.core.materials.model; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.core.materials.BasicWriterUnsafe; -import com.jozufozu.flywheel.util.WriteUnsafe; +import com.jozufozu.flywheel.util.MatrixWrite; public class ModelWriterUnsafe extends BasicWriterUnsafe { @@ -16,7 +16,7 @@ public class ModelWriterUnsafe extends BasicWriterUnsafe { super.writeInternal(d); long ptr = writePointer + 6; - ((WriteUnsafe) (Object) d.model).writeUnsafe(ptr); - ((WriteUnsafe) (Object) d.normal).writeUnsafe(ptr + 4 * 16); + ((MatrixWrite) (Object) d.model).flywheel$writeUnsafe(ptr); + ((MatrixWrite) (Object) d.normal).flywheel$writeUnsafe(ptr + 4 * 16); } } diff --git a/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java b/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java index f0d84bf31..9aacbe054 100644 --- a/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java +++ b/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java @@ -72,6 +72,6 @@ public class RenderLayerEvent extends Event { @Override public String toString() { - return "RenderLayerEvent{" + "world=" + world + ", type=" + type + ", stack=" + stack + ", viewProjection=" + viewProjection + ", buffers=" + buffers + ", camX=" + camX + ", camY=" + camY + ", camZ=" + camZ + ", layer=" + layer + '}'; + return "RenderLayerEvent[" + layer + "][" + "world=" + world + ", type=" + type + ", stack=" + stack + ", viewProjection=" + viewProjection + ", buffers=" + buffers + ", camX=" + camX + ", camY=" + camY + ", camZ=" + camZ + ']'; } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java index 120f552c1..a105affa3 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/BufferBuilderMixin.java @@ -44,7 +44,7 @@ public abstract class BufferBuilderMixin implements BufferBuilderHack { private int vertices; @Override - public void freeBuffer() { + public void flywheel$freeBuffer() { if (this.buffer != null) { MemoryUtil.memFree(this.buffer); this.buffer = null; @@ -52,7 +52,7 @@ public abstract class BufferBuilderMixin implements BufferBuilderHack { } @Override - public void hackBegin(@Nonnull ByteBuffer buffer, @Nonnull VertexFormat format, int vertexCount) { + public void flywheel$hackBegin(@Nonnull ByteBuffer buffer, @Nonnull VertexFormat format, int vertexCount) { this.building = true; this.mode = VertexFormat.Mode.QUADS; diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BufferUploaderAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/BufferUploaderAccessor.java new file mode 100644 index 000000000..8ee045d05 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/BufferUploaderAccessor.java @@ -0,0 +1,16 @@ +package com.jozufozu.flywheel.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import com.mojang.blaze3d.vertex.BufferUploader; + +import net.minecraft.client.renderer.ShaderInstance; + +@Mixin(BufferUploader.class) +public interface BufferUploaderAccessor { + @Accessor("lastVertexArrayObject") + static void flywheel$setLastVAO(int id) { + throw new AssertionError(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererAccessor.java index e348cebbe..f306c68cb 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererAccessor.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererAccessor.java @@ -11,6 +11,6 @@ import net.minecraft.server.level.BlockDestructionProgress; @Mixin(LevelRenderer.class) public interface LevelRendererAccessor { - @Accessor - Long2ObjectMap> getDestructionProgress(); + @Accessor("destructionProgress") + Long2ObjectMap> flywheel$getDestructionProgress(); } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/ShaderInstanceAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/ShaderInstanceAccessor.java index ca5743441..6f7538a4d 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/ShaderInstanceAccessor.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/ShaderInstanceAccessor.java @@ -8,7 +8,7 @@ import net.minecraft.client.renderer.ShaderInstance; @Mixin(ShaderInstance.class) public interface ShaderInstanceAccessor { @Accessor("lastProgramId") - static void setLastProgramId(int id) { + static void flywheel$setLastProgramId(int id) { throw new AssertionError(); } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/atlas/SheetDataAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/atlas/SheetDataAccessor.java index 8ab5bc643..a1914724b 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/atlas/SheetDataAccessor.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/atlas/SheetDataAccessor.java @@ -9,9 +9,9 @@ import net.minecraft.client.renderer.texture.TextureAtlas; public interface SheetDataAccessor { @Accessor("width") - int getWidth(); + int flywheel$getWidth(); @Accessor("height") - int getHeight(); + int flywheel$getHeight(); } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java index fc7dd333c..2967eb43a 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java @@ -5,8 +5,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.util.WriteSafe; -import com.jozufozu.flywheel.util.WriteUnsafe; +import com.jozufozu.flywheel.util.MatrixWrite; import com.mojang.math.Matrix3f; import net.minecraftforge.api.distmarker.Dist; @@ -14,7 +13,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) @Mixin(Matrix3f.class) -public abstract class Matrix3fMixin implements WriteUnsafe, WriteSafe { +public abstract class Matrix3fMixin implements MatrixWrite { @Shadow protected float m00; @Shadow protected float m01; @@ -27,7 +26,7 @@ public abstract class Matrix3fMixin implements WriteUnsafe, WriteSafe { @Shadow protected float m22; @Override - public void writeUnsafe(long ptr) { + public void flywheel$writeUnsafe(long ptr) { MemoryUtil.memPutFloat(ptr, m00); MemoryUtil.memPutFloat(ptr + 4, m10); MemoryUtil.memPutFloat(ptr + 8, m20); @@ -40,7 +39,7 @@ public abstract class Matrix3fMixin implements WriteUnsafe, WriteSafe { } @Override - public void write(VecBuffer buffer) { + public void flywheel$write(VecBuffer buffer) { buffer.putFloat(m00); buffer.putFloat(m10); buffer.putFloat(m20); diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java index 8d98d502e..e512a8899 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java @@ -5,8 +5,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.util.WriteSafe; -import com.jozufozu.flywheel.util.WriteUnsafe; +import com.jozufozu.flywheel.util.MatrixWrite; import com.mojang.math.Matrix4f; import net.minecraftforge.api.distmarker.Dist; @@ -14,7 +13,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) @Mixin(Matrix4f.class) -public abstract class Matrix4fMixin implements WriteUnsafe, WriteSafe { +public abstract class Matrix4fMixin implements MatrixWrite { @Shadow protected float m00; @Shadow protected float m01; @@ -34,7 +33,7 @@ public abstract class Matrix4fMixin implements WriteUnsafe, WriteSafe { @Shadow protected float m33; @Override - public void writeUnsafe(long ptr) { + public void flywheel$writeUnsafe(long ptr) { MemoryUtil.memPutFloat(ptr, m00); MemoryUtil.memPutFloat(ptr + 4, m10); MemoryUtil.memPutFloat(ptr + 8, m20); @@ -54,7 +53,7 @@ public abstract class Matrix4fMixin implements WriteUnsafe, WriteSafe { } @Override - public void write(VecBuffer buf) { + public void flywheel$write(VecBuffer buf) { buf.putFloat(m00); buf.putFloat(m10); buf.putFloat(m20); diff --git a/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java b/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java new file mode 100644 index 000000000..0619d0793 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java @@ -0,0 +1,17 @@ +package com.jozufozu.flywheel.util; + +import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; + +/** + * @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin + * @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin + */ +public interface MatrixWrite { + + /** + * Write the contents of this object into sequential memory starting at the given address. + */ + void flywheel$writeUnsafe(long ptr); + + void flywheel$write(VecBuffer buf); +} diff --git a/src/main/java/com/jozufozu/flywheel/util/WriteSafe.java b/src/main/java/com/jozufozu/flywheel/util/WriteSafe.java deleted file mode 100644 index 0fdc7f128..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/WriteSafe.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.jozufozu.flywheel.util; - -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; - -public interface WriteSafe { - void write(VecBuffer buf); -} diff --git a/src/main/java/com/jozufozu/flywheel/util/WriteUnsafe.java b/src/main/java/com/jozufozu/flywheel/util/WriteUnsafe.java deleted file mode 100644 index 1b4b6e985..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/WriteUnsafe.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jozufozu.flywheel.util; - -public interface WriteUnsafe { - - /** - * Write the contents of this object into sequential memory starting at the given address. - */ - void writeUnsafe(long ptr); -} diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index c2e308cef..9113d4962 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -6,6 +6,7 @@ "refmap": "flywheel.refmap.json", "client": [ "BufferBuilderMixin", + "BufferUploaderAccessor", "CancelEntityRenderMixin", "ChunkRebuildHooksMixin", "FixFabulousDepthMixin", From 6c40b8be0b0f863ca0b8b462c77889a1c9b1e10c Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 24 Dec 2021 02:21:59 -0800 Subject: [PATCH 21/22] Repack JOML --- build.gradle | 2 - .../flywheel/repack/joml/AxisAngle4d.java | 906 + .../flywheel/repack/joml/AxisAngle4f.java | 813 + .../repack/joml/ConfigurationException.java | 36 + .../repack/joml/FrustumIntersection.java | 971 + .../repack/joml/FrustumRayBuilder.java | 154 + .../flywheel/repack/joml/GeometryUtils.java | 247 + .../flywheel/repack/joml/Interpolationd.java | 337 + .../flywheel/repack/joml/Interpolationf.java | 337 + .../flywheel/repack/joml/Intersectiond.java | 4789 +++++ .../flywheel/repack/joml/Intersectionf.java | 4789 +++++ .../jozufozu/flywheel/repack/joml/Math.java | 566 + .../flywheel/repack/joml/Matrix2d.java | 1536 ++ .../flywheel/repack/joml/Matrix2dc.java | 725 + .../flywheel/repack/joml/Matrix2f.java | 1429 ++ .../flywheel/repack/joml/Matrix2fc.java | 709 + .../flywheel/repack/joml/Matrix3d.java | 5582 ++++++ .../flywheel/repack/joml/Matrix3dStack.java | 186 + .../flywheel/repack/joml/Matrix3dc.java | 2310 +++ .../flywheel/repack/joml/Matrix3f.java | 4920 +++++ .../flywheel/repack/joml/Matrix3fStack.java | 186 + .../flywheel/repack/joml/Matrix3fc.java | 2199 ++ .../flywheel/repack/joml/Matrix3x2d.java | 2497 +++ .../flywheel/repack/joml/Matrix3x2dStack.java | 186 + .../flywheel/repack/joml/Matrix3x2dc.java | 1196 ++ .../flywheel/repack/joml/Matrix3x2f.java | 2492 +++ .../flywheel/repack/joml/Matrix3x2fStack.java | 186 + .../flywheel/repack/joml/Matrix3x2fc.java | 1180 ++ .../flywheel/repack/joml/Matrix4d.java | 16604 ++++++++++++++++ .../flywheel/repack/joml/Matrix4dStack.java | 185 + .../flywheel/repack/joml/Matrix4dc.java | 6289 ++++++ .../flywheel/repack/joml/Matrix4f.java | 15355 ++++++++++++++ .../flywheel/repack/joml/Matrix4fStack.java | 185 + .../flywheel/repack/joml/Matrix4fc.java | 6082 ++++++ .../flywheel/repack/joml/Matrix4x3d.java | 10698 ++++++++++ .../flywheel/repack/joml/Matrix4x3dStack.java | 186 + .../flywheel/repack/joml/Matrix4x3dc.java | 3821 ++++ .../flywheel/repack/joml/Matrix4x3f.java | 9838 +++++++++ .../flywheel/repack/joml/Matrix4x3fStack.java | 186 + .../flywheel/repack/joml/Matrix4x3fc.java | 3544 ++++ .../flywheel/repack/joml/MemUtil.java | 5572 ++++++ .../flywheel/repack/joml/Options.java | 116 + .../repack/joml/PolygonsIntersection.java | 328 + .../flywheel/repack/joml/Quaterniond.java | 2985 +++ .../repack/joml/QuaterniondInterpolator.java | 354 + .../flywheel/repack/joml/Quaterniondc.java | 1966 ++ .../flywheel/repack/joml/Quaternionf.java | 3066 +++ .../repack/joml/QuaternionfInterpolator.java | 354 + .../flywheel/repack/joml/Quaternionfc.java | 2105 ++ .../jozufozu/flywheel/repack/joml/Random.java | 169 + .../repack/joml/RayAabIntersection.java | 399 + .../flywheel/repack/joml/RoundingMode.java | 60 + .../flywheel/repack/joml/Runtime.java | 148 + .../flywheel/repack/joml/SimplexNoise.java | 485 + .../flywheel/repack/joml/Vector2d.java | 1364 ++ .../flywheel/repack/joml/Vector2dc.java | 670 + .../flywheel/repack/joml/Vector2f.java | 1252 ++ .../flywheel/repack/joml/Vector2fc.java | 609 + .../flywheel/repack/joml/Vector2i.java | 965 + .../flywheel/repack/joml/Vector2ic.java | 391 + .../flywheel/repack/joml/Vector3d.java | 2613 +++ .../flywheel/repack/joml/Vector3dc.java | 1392 ++ .../flywheel/repack/joml/Vector3f.java | 2086 ++ .../flywheel/repack/joml/Vector3fc.java | 1089 + .../flywheel/repack/joml/Vector3i.java | 1131 ++ .../flywheel/repack/joml/Vector3ic.java | 409 + .../flywheel/repack/joml/Vector4d.java | 2129 ++ .../flywheel/repack/joml/Vector4dc.java | 933 + .../flywheel/repack/joml/Vector4f.java | 1939 ++ .../flywheel/repack/joml/Vector4fc.java | 838 + .../flywheel/repack/joml/Vector4i.java | 1212 ++ .../flywheel/repack/joml/Vector4ic.java | 432 + .../flywheel/repack/joml/package.html | 14 + .../joml/sampling/BestCandidateSampling.java | 1034 + .../repack/joml/sampling/Callback2d.java | 41 + .../repack/joml/sampling/Callback3d.java | 43 + .../repack/joml/sampling/Convolution.java | 116 + .../flywheel/repack/joml/sampling/Math.java | 64 + .../repack/joml/sampling/PoissonSampling.java | 161 + .../repack/joml/sampling/SpiralSampling.java | 101 + .../joml/sampling/StratifiedSampling.java | 93 + .../repack/joml/sampling/UniformSampling.java | 121 + .../repack/joml/sampling/package.html | 8 + 83 files changed, 155794 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/AxisAngle4d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/AxisAngle4f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/ConfigurationException.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/FrustumIntersection.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/FrustumRayBuilder.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/GeometryUtils.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Interpolationd.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Interpolationf.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Intersectiond.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Intersectionf.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Math.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2dc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2fc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3dStack.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3dc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3fStack.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3fc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2dStack.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2dc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2fStack.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2fc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4dStack.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4dc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4fStack.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4fc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3dStack.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3dc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3fStack.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3fc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/MemUtil.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Options.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/PolygonsIntersection.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Quaterniond.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/QuaterniondInterpolator.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Quaterniondc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Quaternionf.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/QuaternionfInterpolator.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Quaternionfc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Random.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/RayAabIntersection.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/RoundingMode.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Runtime.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/SimplexNoise.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector2d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector2dc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector2f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector2fc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector2i.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector2ic.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector3d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector3dc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector3f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector3fc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector3i.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector3ic.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector4d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector4dc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector4f.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector4fc.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector4i.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/Vector4ic.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/package.html create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/BestCandidateSampling.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Callback2d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Callback3d.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Convolution.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Math.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/PoissonSampling.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/SpiralSampling.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/StratifiedSampling.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/UniformSampling.java create mode 100644 src/main/java/com/jozufozu/flywheel/repack/joml/sampling/package.html diff --git a/build.gradle b/build.gradle index f3a3818e2..a45bf118b 100644 --- a/build.gradle +++ b/build.gradle @@ -101,8 +101,6 @@ repositories { dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" - //implementation 'org.joml:joml:1.10.1' - // https://discord.com/channels/313125603924639766/725850371834118214/910619168821354497 // Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings // This allows 'Settings > Build, Execution, and Deployment > Build Tools > Gradle > Build and run using' set to IntelliJ to work correctly diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/AxisAngle4d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/AxisAngle4d.java new file mode 100644 index 000000000..91e5baf1e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/AxisAngle4d.java @@ -0,0 +1,906 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Represents a 3D rotation of a given radians about an axis represented as an + * unit 3D vector. + *

+ * This class uses double-precision components. + * + * @author Kai Burjack + */ +public class AxisAngle4d implements Externalizable, Cloneable { + + private static final long serialVersionUID = 1L; + + /** + * The angle in radians. + */ + public double angle; + /** + * The x-component of the rotation axis. + */ + public double x; + /** + * The y-component of the rotation axis. + */ + public double y; + /** + * The z-component of the rotation axis. + */ + public double z; + + /** + * Create a new {@link AxisAngle4d} with zero rotation about (0, 0, 1). + */ + public AxisAngle4d() { + z = 1.0; + } + + /** + * Create a new {@link AxisAngle4d} with the same values of a. + * + * @param a + * the AngleAxis4d to copy the values from + */ + public AxisAngle4d(AxisAngle4d a) { + x = a.x; + y = a.y; + z = a.z; + angle = (a.angle < 0.0 ? Math.PI + Math.PI + a.angle % (Math.PI + Math.PI) : a.angle) % (Math.PI + Math.PI); + } + + /** + * Create a new {@link AxisAngle4d} with the same values of a. + * + * @param a + * the AngleAxis4f to copy the values from + */ + public AxisAngle4d(AxisAngle4f a) { + x = a.x; + y = a.y; + z = a.z; + angle = (a.angle < 0.0 ? Math.PI + Math.PI + a.angle % (Math.PI + Math.PI) : a.angle) % (Math.PI + Math.PI); + } + + /** + * Create a new {@link AxisAngle4d} from the given {@link Quaternionfc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param q + * the quaternion from which to create the new AngleAxis4f + */ + public AxisAngle4d(Quaternionfc q) { + float acos = Math.safeAcos(q.w()); + float invSqrt = Math.invsqrt(1.0f - q.w() * q.w()); + if (Float.isInfinite(invSqrt)) { + this.x = 0.0; + this.y = 0.0; + this.z = 1.0; + } else { + this.x = q.x() * invSqrt; + this.y = q.y() * invSqrt; + this.z = q.z() * invSqrt; + } + this.angle = acos + acos; + } + + /** + * Create a new {@link AxisAngle4d} from the given {@link Quaterniondc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param q + * the quaternion from which to create the new AngleAxis4d + */ + public AxisAngle4d(Quaterniondc q) { + double acos = Math.safeAcos(q.w()); + double invSqrt = Math.invsqrt(1.0 - q.w() * q.w()); + if (Double.isInfinite(invSqrt)) { + this.x = 0.0; + this.y = 0.0; + this.z = 1.0; + } else { + this.x = q.x() * invSqrt; + this.y = q.y() * invSqrt; + this.z = q.z() * invSqrt; + } + this.angle = acos + acos; + } + + /** + * Create a new {@link AxisAngle4d} with the given values. + * + * @param angle + * the angle in radians + * @param x + * the x-coordinate of the rotation axis + * @param y + * the y-coordinate of the rotation axis + * @param z + * the z-coordinate of the rotation axis + */ + public AxisAngle4d(double angle, double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + this.angle = (angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI); + } + + /** + * Create a new {@link AxisAngle4d} with the given values. + * + * @param angle the angle in radians + * @param v the rotation axis as a {@link Vector3dc} + */ + public AxisAngle4d(double angle, Vector3dc v) { + this(angle, v.x(), v.y(), v.z()); + } + + /** + * Create a new {@link AxisAngle4d} with the given values. + * + * @param angle the angle in radians + * @param v the rotation axis as a {@link Vector3f} + */ + public AxisAngle4d(double angle, Vector3f v) { + this(angle, v.x, v.y, v.z); + } + + /** + * Set this {@link AxisAngle4d} to the values of a. + * + * @param a + * the AngleAxis4f to copy the values from + * @return this + */ + public AxisAngle4d set(AxisAngle4d a) { + x = a.x; + y = a.y; + z = a.z; + angle = (a.angle < 0.0 ? Math.PI + Math.PI + a.angle % (Math.PI + Math.PI) : a.angle) % (Math.PI + Math.PI); + return this; + } + + /** + * Set this {@link AxisAngle4d} to the values of a. + * + * @param a + * the AngleAxis4f to copy the values from + * @return this + */ + public AxisAngle4d set(AxisAngle4f a) { + x = a.x; + y = a.y; + z = a.z; + angle = (a.angle < 0.0 ? Math.PI + Math.PI + a.angle % (Math.PI + Math.PI) : a.angle) % (Math.PI + Math.PI); + return this; + } + + /** + * Set this {@link AxisAngle4d} to the given values. + * + * @param angle + * the angle in radians + * @param x + * the x-coordinate of the rotation axis + * @param y + * the y-coordinate of the rotation axis + * @param z + * the z-coordinate of the rotation axis + * @return this + */ + public AxisAngle4d set(double angle, double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + this.angle = (angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI); + return this; + } + + /** + * Set this {@link AxisAngle4d} to the given values. + * + * @param angle + * the angle in radians + * @param v + * the rotation axis as a {@link Vector3dc} + * @return this + */ + public AxisAngle4d set(double angle, Vector3dc v) { + return set(angle, v.x(), v.y(), v.z()); + } + + /** + * Set this {@link AxisAngle4d} to the given values. + * + * @param angle + * the angle in radians + * @param v + * the rotation axis as a {@link Vector3f} + * @return this + */ + public AxisAngle4d set(double angle, Vector3f v) { + return set(angle, v.x, v.y, v.z); + } + + /** + * Set this {@link AxisAngle4d} to be equivalent to the given + * {@link Quaternionfc}. + * + * @param q + * the quaternion to set this AngleAxis4d from + * @return this + */ + public AxisAngle4d set(Quaternionfc q) { + float acos = Math.safeAcos(q.w()); + float invSqrt = Math.invsqrt(1.0f - q.w() * q.w()); + if (Float.isInfinite(invSqrt)) { + this.x = 0.0; + this.y = 0.0; + this.z = 1.0; + } else { + this.x = q.x() * invSqrt; + this.y = q.y() * invSqrt; + this.z = q.z() * invSqrt; + } + this.angle = acos + acos; + return this; + } + + /** + * Set this {@link AxisAngle4d} to be equivalent to the given + * {@link Quaterniondc}. + * + * @param q + * the quaternion to set this AngleAxis4d from + * @return this + */ + public AxisAngle4d set(Quaterniondc q) { + double acos = Math.safeAcos(q.w()); + double invSqrt = Math.invsqrt(1.0f - q.w() * q.w()); + if (Double.isInfinite(invSqrt)) { + this.x = 0.0; + this.y = 0.0; + this.z = 1.0; + } else { + this.x = q.x() * invSqrt; + this.y = q.y() * invSqrt; + this.z = q.z() * invSqrt; + } + this.angle = acos + acos; + return this; + } + + /** + * Set this {@link AxisAngle4d} to be equivalent to the rotation + * of the given {@link Matrix3fc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix3fc to set this AngleAxis4d from + * @return this + */ + public AxisAngle4d set(Matrix3fc m) { + double nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + double nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + double nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + double lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + double lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + double lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + double epsilon = 1E-4, epsilon2 = 1E-3; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = Math.PI; + double xx = (nm00 + 1) / 2; + double yy = (nm11 + 1) / 2; + double zz = (nm22 + 1) / 2; + double xy = (nm10 + nm01) / 4; + double xz = (nm20 + nm02) / 4; + double yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } else if (yy > zz) { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + return this; + } + double s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (nm12 - nm21) / s; + y = (nm20 - nm02) / s; + z = (nm01 - nm10) / s; + return this; + } + + /** + * Set this {@link AxisAngle4d} to be equivalent to the rotation + * of the given {@link Matrix3dc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix3dc to set this AngleAxis4d from + * @return this + */ + public AxisAngle4d set(Matrix3dc m) { + double nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + double nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + double nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + double lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + double lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + double lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + double epsilon = 1E-4, epsilon2 = 1E-3; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = Math.PI; + double xx = (nm00 + 1) / 2; + double yy = (nm11 + 1) / 2; + double zz = (nm22 + 1) / 2; + double xy = (nm10 + nm01) / 4; + double xz = (nm20 + nm02) / 4; + double yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } else if (yy > zz) { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + return this; + } + double s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (nm12 - nm21) / s; + y = (nm20 - nm02) / s; + z = (nm01 - nm10) / s; + return this; + } + + /** + * Set this {@link AxisAngle4d} to be equivalent to the rotational component + * of the given {@link Matrix4fc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix4fc to set this AngleAxis4d from + * @return this + */ + public AxisAngle4d set(Matrix4fc m) { + double nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + double nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + double nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + double lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + double lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + double lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + double epsilon = 1E-4, epsilon2 = 1E-3; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = Math.PI; + double xx = (nm00 + 1) / 2; + double yy = (nm11 + 1) / 2; + double zz = (nm22 + 1) / 2; + double xy = (nm10 + nm01) / 4; + double xz = (nm20 + nm02) / 4; + double yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } else if (yy > zz) { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + return this; + } + double s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (nm12 - nm21) / s; + y = (nm20 - nm02) / s; + z = (nm01 - nm10) / s; + return this; + } + + /** + * Set this {@link AxisAngle4d} to be equivalent to the rotational component + * of the given {@link Matrix4x3fc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix4x3fc to set this AngleAxis4d from + * @return this + */ + public AxisAngle4d set(Matrix4x3fc m) { + double nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + double nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + double nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + double lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + double lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + double lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + double epsilon = 1E-4, epsilon2 = 1E-3; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = Math.PI; + double xx = (nm00 + 1) / 2; + double yy = (nm11 + 1) / 2; + double zz = (nm22 + 1) / 2; + double xy = (nm10 + nm01) / 4; + double xz = (nm20 + nm02) / 4; + double yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } else if (yy > zz) { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + return this; + } + double s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (nm12 - nm21) / s; + y = (nm20 - nm02) / s; + z = (nm01 - nm10) / s; + return this; + } + + /** + * Set this {@link AxisAngle4d} to be equivalent to the rotational component + * of the given {@link Matrix4dc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix4dc to set this AngleAxis4d from + * @return this + */ + public AxisAngle4d set(Matrix4dc m) { + double nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + double nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + double nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + double lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + double lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + double lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + double epsilon = 1E-4, epsilon2 = 1E-3; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = Math.PI; + double xx = (nm00 + 1) / 2; + double yy = (nm11 + 1) / 2; + double zz = (nm22 + 1) / 2; + double xy = (nm10 + nm01) / 4; + double xz = (nm20 + nm02) / 4; + double yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } else if (yy > zz) { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + return this; + } + double s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (nm12 - nm21) / s; + y = (nm20 - nm02) / s; + z = (nm01 - nm10) / s; + return this; + } + + /** + * Set the given {@link Quaternionf} to be equivalent to this {@link AxisAngle4d} rotation. + * + * @see Quaternionf#set(AxisAngle4d) + * + * @param q + * the quaternion to set + * @return q + */ + public Quaternionf get(Quaternionf q) { + return q.set(this); + } + + /** + * Set the given {@link Quaterniond} to be equivalent to this {@link AxisAngle4d} rotation. + * + * @see Quaterniond#set(AxisAngle4d) + * + * @param q + * the quaternion to set + * @return q + */ + public Quaterniond get(Quaterniond q) { + return q.set(this); + } + + /** + * Set the given {@link Matrix4f} to a rotation transformation equivalent to this {@link AxisAngle4d}. + * + * @see Matrix4f#set(AxisAngle4d) + * + * @param m + * the matrix to set + * @return m + */ + public Matrix4f get(Matrix4f m) { + return m.set(this); + } + + /** + * Set the given {@link Matrix3f} to a rotation transformation equivalent to this {@link AxisAngle4d}. + * + * @see Matrix3f#set(AxisAngle4d) + * + * @param m + * the matrix to set + * @return m + */ + public Matrix3f get(Matrix3f m) { + return m.set(this); + } + + /** + * Set the given {@link Matrix4d} to a rotation transformation equivalent to this {@link AxisAngle4d}. + * + * @see Matrix4f#set(AxisAngle4d) + * + * @param m + * the matrix to set + * @return m + */ + public Matrix4d get(Matrix4d m) { + return m.set(this); + } + + /** + * Set the given {@link Matrix3d} to a rotation transformation equivalent to this {@link AxisAngle4d}. + * + * @see Matrix3f#set(AxisAngle4d) + * + * @param m + * the matrix to set + * @return m + */ + public Matrix3d get(Matrix3d m) { + return m.set(this); + } + + /** + * Set the given {@link AxisAngle4d} to this {@link AxisAngle4d}. + * + * @param dest + * will hold the result + * @return dest + */ + public AxisAngle4d get(AxisAngle4d dest) { + return dest.set(this); + } + + /** + * Set the given {@link AxisAngle4f} to this {@link AxisAngle4d}. + * + * @param dest + * will hold the result + * @return dest + */ + public AxisAngle4f get(AxisAngle4f dest) { + return dest.set(this); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(angle); + out.writeDouble(x); + out.writeDouble(y); + out.writeDouble(z); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + angle = in.readDouble(); + x = in.readDouble(); + y = in.readDouble(); + z = in.readDouble(); + } + + /** + * Normalize the axis vector. + * + * @return this + */ + public AxisAngle4d normalize() { + double invLength = Math.invsqrt(x * x + y * y + z * z); + x *= invLength; + y *= invLength; + z *= invLength; + return this; + } + + /** + * Increase the rotation angle by the given amount. + *

+ * This method also takes care of wrapping around. + * + * @param ang + * the angle increase + * @return this + */ + public AxisAngle4d rotate(double ang) { + angle += ang; + angle = (angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI); + return this; + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4d}. + * + * @param v + * the vector to transform + * @return v + */ + public Vector3d transform(Vector3d v) { + return transform(v, v); + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4d} + * and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector3d transform(Vector3dc v, Vector3d dest) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + double dot = x * v.x() + y * v.y() + z * v.z(); + dest.set(v.x() * cos + sin * (y * v.z() - z * v.y()) + (1.0 - cos) * dot * x, + v.y() * cos + sin * (z * v.x() - x * v.z()) + (1.0 - cos) * dot * y, + v.z() * cos + sin * (x * v.y() - y * v.x()) + (1.0 - cos) * dot * z); + return dest; + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4d}. + * + * @param v + * the vector to transform + * @return v + */ + public Vector3f transform(Vector3f v) { + return transform(v, v); + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4d} + * and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector3f transform(Vector3fc v, Vector3f dest) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + double dot = x * v.x() + y * v.y() + z * v.z(); + dest.set((float) (v.x() * cos + sin * (y * v.z() - z * v.y()) + (1.0 - cos) * dot * x), + (float) (v.y() * cos + sin * (z * v.x() - x * v.z()) + (1.0 - cos) * dot * y), + (float) (v.z() * cos + sin * (x * v.y() - y * v.x()) + (1.0 - cos) * dot * z)); + return dest; + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4d}. + * + * @param v + * the vector to transform + * @return v + */ + public Vector4d transform(Vector4d v) { + return transform(v, v); + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4d} + * and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector4d transform(Vector4dc v, Vector4d dest) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + double dot = x * v.x() + y * v.y() + z * v.z(); + dest.set(v.x() * cos + sin * (y * v.z() - z * v.y()) + (1.0 - cos) * dot * x, + v.y() * cos + sin * (z * v.x() - x * v.z()) + (1.0 - cos) * dot * y, + v.z() * cos + sin * (x * v.y() - y * v.x()) + (1.0 - cos) * dot * z, + dest.w); + return dest; + } + + /** + * Return a string representation of this {@link AxisAngle4d}. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string " 0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this {@link AxisAngle4d} by formatting the components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + " " + Runtime.format(z, formatter) + " <| " + Runtime.format(angle, formatter) + ")"; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(x); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(y); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(z); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AxisAngle4d other = (AxisAngle4d) obj; + if (Double.doubleToLongBits((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)) != + Double.doubleToLongBits((other.angle < 0.0 ? Math.PI + Math.PI + other.angle % (Math.PI + Math.PI) : other.angle) % (Math.PI + Math.PI))) + return false; + if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) + return false; + if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) + return false; + if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z)) + return false; + return true; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/AxisAngle4f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/AxisAngle4f.java new file mode 100644 index 000000000..ba471caa2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/AxisAngle4f.java @@ -0,0 +1,813 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Represents a 3D rotation of a given radians about an axis represented as an + * unit 3D vector. + *

+ * This class uses single-precision components. + * + * @author Kai Burjack + */ +public class AxisAngle4f implements Externalizable, Cloneable { + + private static final long serialVersionUID = 1L; + + /** + * The angle in radians. + */ + public float angle; + /** + * The x-component of the rotation axis. + */ + public float x; + /** + * The y-component of the rotation axis. + */ + public float y; + /** + * The z-component of the rotation axis. + */ + public float z; + + /** + * Create a new {@link AxisAngle4f} with zero rotation about (0, 0, 1). + */ + public AxisAngle4f() { + z = 1.0f; + } + + /** + * Create a new {@link AxisAngle4f} with the same values of a. + * + * @param a + * the AngleAxis4f to copy the values from + */ + public AxisAngle4f(AxisAngle4f a) { + x = a.x; + y = a.y; + z = a.z; + angle = (float) ((a.angle < 0.0 ? Math.PI + Math.PI + a.angle % (Math.PI + Math.PI) : a.angle) % (Math.PI + Math.PI)); + } + + /** + * Create a new {@link AxisAngle4f} from the given {@link Quaternionfc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param q + * the quaternion from which to create the new AngleAxis4f + */ + public AxisAngle4f(Quaternionfc q) { + float acos = Math.safeAcos(q.w()); + float invSqrt = Math.invsqrt(1.0f - q.w() * q.w()); + if (Float.isInfinite(invSqrt)) { + this.x = 0.0f; + this.y = 0.0f; + this.z = 1.0f; + } else { + this.x = q.x() * invSqrt; + this.y = q.y() * invSqrt; + this.z = q.z() * invSqrt; + } + this.angle = acos + acos; + } + + /** + * Create a new {@link AxisAngle4f} with the given values. + * + * @param angle + * the angle in radians + * @param x + * the x-coordinate of the rotation axis + * @param y + * the y-coordinate of the rotation axis + * @param z + * the z-coordinate of the rotation axis + */ + public AxisAngle4f(float angle, float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + this.angle = (float) ((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)); + } + + /** + * Create a new {@link AxisAngle4f} with the given values. + * + * @param angle the angle in radians + * @param v the rotation axis as a {@link Vector3f} + */ + public AxisAngle4f(float angle, Vector3fc v) { + this(angle, v.x(), v.y(), v.z()); + } + + /** + * Set this {@link AxisAngle4f} to the values of a. + * + * @param a + * the AngleAxis4f to copy the values from + * @return this + */ + public AxisAngle4f set(AxisAngle4f a) { + x = a.x; + y = a.y; + z = a.z; + angle = a.angle; + angle = (float) ((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)); + return this; + } + + /** + * Set this {@link AxisAngle4f} to the values of a. + * + * @param a + * the AngleAxis4d to copy the values from + * @return this + */ + public AxisAngle4f set(AxisAngle4d a) { + x = (float) a.x; + y = (float) a.y; + z = (float) a.z; + angle = (float) a.angle; + angle = (float) ((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)); + return this; + } + + /** + * Set this {@link AxisAngle4f} to the given values. + * + * @param angle + * the angle in radians + * @param x + * the x-coordinate of the rotation axis + * @param y + * the y-coordinate of the rotation axis + * @param z + * the z-coordinate of the rotation axis + * @return this + */ + public AxisAngle4f set(float angle, float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + this.angle = (float) ((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)); + return this; + } + + /** + * Set this {@link AxisAngle4f} to the given values. + * + * @param angle + * the angle in radians + * @param v + * the rotation axis as a {@link Vector3f} + * @return this + */ + public AxisAngle4f set(float angle, Vector3fc v) { + return set(angle, v.x(), v.y(), v.z()); + } + + /** + * Set this {@link AxisAngle4f} to be equivalent to the given + * {@link Quaternionfc}. + * + * @param q + * the quaternion to set this AngleAxis4f from + * @return this + */ + public AxisAngle4f set(Quaternionfc q) { + float acos = Math.safeAcos(q.w()); + float invSqrt = Math.invsqrt(1.0f - q.w() * q.w()); + if (Float.isInfinite(invSqrt)) { + this.x = 0.0f; + this.y = 0.0f; + this.z = 1.0f; + } else { + this.x = q.x() * invSqrt; + this.y = q.y() * invSqrt; + this.z = q.z() * invSqrt; + } + this.angle = acos + acos; + return this; + } + + /** + * Set this {@link AxisAngle4f} to be equivalent to the given + * {@link Quaterniondc}. + * + * @param q + * the quaternion to set this AngleAxis4f from + * @return this + */ + public AxisAngle4f set(Quaterniondc q) { + double acos = Math.safeAcos(q.w()); + double invSqrt = Math.invsqrt(1.0 - q.w() * q.w()); + if (Double.isInfinite(invSqrt)) { + this.x = 0.0f; + this.y = 0.0f; + this.z = 1.0f; + } else { + this.x = (float) (q.x() * invSqrt); + this.y = (float) (q.y() * invSqrt); + this.z = (float) (q.z() * invSqrt); + } + this.angle = (float) (acos + acos); + return this; + } + + /** + * Set this {@link AxisAngle4f} to be equivalent to the rotation + * of the given {@link Matrix3fc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix3fc to set this AngleAxis4f from + * @return this + */ + public AxisAngle4f set(Matrix3fc m) { + float nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + float nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + float nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + float lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + float lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + float lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + float epsilon = 1E-4f, epsilon2 = 1E-3f; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = Math.PI_f; + float xx = (nm00 + 1) / 2; + float yy = (nm11 + 1) / 2; + float zz = (nm22 + 1) / 2; + float xy = (nm10 + nm01) / 4; + float xz = (nm20 + nm02) / 4; + float yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } else if (yy > zz) { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + return this; + } + float s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (nm12 - nm21) / s; + y = (nm20 - nm02) / s; + z = (nm01 - nm10) / s; + return this; + } + + /** + * Set this {@link AxisAngle4f} to be equivalent to the rotation + * of the given {@link Matrix3dc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix3d to set this AngleAxis4f from + * @return this + */ + public AxisAngle4f set(Matrix3dc m) { + double nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + double nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + double nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + double lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + double lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + double lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + double epsilon = 1E-4, epsilon2 = 1E-3; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = (float) Math.PI; + double xx = (nm00 + 1) / 2; + double yy = (nm11 + 1) / 2; + double zz = (nm22 + 1) / 2; + double xy = (nm10 + nm01) / 4; + double xz = (nm20 + nm02) / 4; + double yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = (float) Math.sqrt(xx); + y = (float) (xy / x); + z = (float) (xz / x); + } else if (yy > zz) { + y = (float) Math.sqrt(yy); + x = (float) (xy / y); + z = (float) (yz / y); + } else { + z = (float) Math.sqrt(zz); + x = (float) (xz / z); + y = (float) (yz / z); + } + return this; + } + double s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = (float) Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (float) ((nm12 - nm21) / s); + y = (float) ((nm20 - nm02) / s); + z = (float) ((nm01 - nm10) / s); + return this; + } + + /** + * Set this {@link AxisAngle4f} to be equivalent to the rotational component + * of the given {@link Matrix4fc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix4fc to set this AngleAxis4f from + * @return this + */ + public AxisAngle4f set(Matrix4fc m) { + float nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + float nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + float nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + float lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + float lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + float lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + float epsilon = 1E-4f, epsilon2 = 1E-3f; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = Math.PI_f; + float xx = (nm00 + 1) / 2; + float yy = (nm11 + 1) / 2; + float zz = (nm22 + 1) / 2; + float xy = (nm10 + nm01) / 4; + float xz = (nm20 + nm02) / 4; + float yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } else if (yy > zz) { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + return this; + } + float s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (nm12 - nm21) / s; + y = (nm20 - nm02) / s; + z = (nm01 - nm10) / s; + return this; + } + + /** + * Set this {@link AxisAngle4f} to be equivalent to the rotational component + * of the given {@link Matrix4x3fc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix4x3fc to set this AngleAxis4f from + * @return this + */ + public AxisAngle4f set(Matrix4x3fc m) { + float nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + float nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + float nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + float lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + float lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + float lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + float epsilon = 1E-4f, epsilon2 = 1E-3f; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = Math.PI_f; + float xx = (nm00 + 1) / 2; + float yy = (nm11 + 1) / 2; + float zz = (nm22 + 1) / 2; + float xy = (nm10 + nm01) / 4; + float xz = (nm20 + nm02) / 4; + float yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } else if (yy > zz) { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + return this; + } + float s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (nm12 - nm21) / s; + y = (nm20 - nm02) / s; + z = (nm01 - nm10) / s; + return this; + } + + /** + * Set this {@link AxisAngle4f} to be equivalent to the rotational component + * of the given {@link Matrix4dc}. + *

+ * Reference: http://www.euclideanspace.com + * + * @param m + * the Matrix4dc to set this AngleAxis4f from + * @return this + */ + public AxisAngle4f set(Matrix4dc m) { + double nm00 = m.m00(), nm01 = m.m01(), nm02 = m.m02(); + double nm10 = m.m10(), nm11 = m.m11(), nm12 = m.m12(); + double nm20 = m.m20(), nm21 = m.m21(), nm22 = m.m22(); + double lenX = Math.invsqrt(m.m00() * m.m00() + m.m01() * m.m01() + m.m02() * m.m02()); + double lenY = Math.invsqrt(m.m10() * m.m10() + m.m11() * m.m11() + m.m12() * m.m12()); + double lenZ = Math.invsqrt(m.m20() * m.m20() + m.m21() * m.m21() + m.m22() * m.m22()); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + double epsilon = 1E-4, epsilon2 = 1E-3; + if (Math.abs(nm10 - nm01) < epsilon && Math.abs(nm20 - nm02) < epsilon && Math.abs(nm21 - nm12) < epsilon) { + if (Math.abs(nm10 + nm01) < epsilon2 && Math.abs(nm20 + nm02) < epsilon2 && Math.abs(nm21 + nm12) < epsilon2 + && Math.abs(nm00 + nm11 + nm22 - 3) < epsilon2) { + x = 0; + y = 0; + z = 1; + angle = 0; + return this; + } + angle = (float) Math.PI; + double xx = (nm00 + 1) / 2; + double yy = (nm11 + 1) / 2; + double zz = (nm22 + 1) / 2; + double xy = (nm10 + nm01) / 4; + double xz = (nm20 + nm02) / 4; + double yz = (nm21 + nm12) / 4; + if ((xx > yy) && (xx > zz)) { + x = (float) Math.sqrt(xx); + y = (float) (xy / x); + z = (float) (xz / x); + } else if (yy > zz) { + y = (float) Math.sqrt(yy); + x = (float) (xy / y); + z = (float) (yz / y); + } else { + z = (float) Math.sqrt(zz); + x = (float) (xz / z); + y = (float) (yz / z); + } + return this; + } + double s = Math.sqrt((nm12 - nm21) * (nm12 - nm21) + (nm20 - nm02) * (nm20 - nm02) + (nm01 - nm10) * (nm01 - nm10)); + angle = (float) Math.safeAcos((nm00 + nm11 + nm22 - 1) / 2); + x = (float) ((nm12 - nm21) / s); + y = (float) ((nm20 - nm02) / s); + z = (float) ((nm01 - nm10) / s); + return this; + } + + /** + * Set the given {@link Quaternionf} to be equivalent to this {@link AxisAngle4f} rotation. + * + * @see Quaternionf#set(AxisAngle4f) + * + * @param q + * the quaternion to set + * @return q + */ + public Quaternionf get(Quaternionf q) { + return q.set(this); + } + + /** + * Set the given {@link Quaterniond} to be equivalent to this {@link AxisAngle4f} rotation. + * + * @see Quaterniond#set(AxisAngle4f) + * + * @param q + * the quaternion to set + * @return q + */ + public Quaterniond get(Quaterniond q) { + return q.set(this); + } + + /** + * Set the given {@link Matrix4f} to a rotation transformation equivalent to this {@link AxisAngle4f}. + * + * @see Matrix4f#set(AxisAngle4f) + * + * @param m + * the matrix to set + * @return m + */ + public Matrix4f get(Matrix4f m) { + return m.set(this); + } + + /** + * Set the given {@link Matrix3f} to a rotation transformation equivalent to this {@link AxisAngle4f}. + * + * @see Matrix3f#set(AxisAngle4f) + * + * @param m + * the matrix to set + * @return m + */ + public Matrix3f get(Matrix3f m) { + return m.set(this); + } + + /** + * Set the given {@link Matrix4d} to a rotation transformation equivalent to this {@link AxisAngle4f}. + * + * @see Matrix4f#set(AxisAngle4f) + * + * @param m + * the matrix to set + * @return m + */ + public Matrix4d get(Matrix4d m) { + return m.set(this); + } + + /** + * Set the given {@link Matrix3d} to a rotation transformation equivalent to this {@link AxisAngle4f}. + * + * @see Matrix3f#set(AxisAngle4f) + * + * @param m + * the matrix to set + * @return m + */ + public Matrix3d get(Matrix3d m) { + return m.set(this); + } + + /** + * Set the given {@link AxisAngle4d} to this {@link AxisAngle4f}. + * + * @param dest + * will hold the result + * @return dest + */ + public AxisAngle4d get(AxisAngle4d dest) { + return dest.set(this); + } + + /** + * Set the given {@link AxisAngle4f} to this {@link AxisAngle4f}. + * + * @param dest + * will hold the result + * @return dest + */ + public AxisAngle4f get(AxisAngle4f dest) { + return dest.set(this); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(angle); + out.writeFloat(x); + out.writeFloat(y); + out.writeFloat(z); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + angle = in.readFloat(); + x = in.readFloat(); + y = in.readFloat(); + z = in.readFloat(); + } + + /** + * Normalize the axis vector. + * + * @return this + */ + public AxisAngle4f normalize() { + float invLength = Math.invsqrt(x * x + y * y + z * z); + x *= invLength; + y *= invLength; + z *= invLength; + return this; + } + + /** + * Increase the rotation angle by the given amount. + *

+ * This method also takes care of wrapping around. + * + * @param ang + * the angle increase + * @return this + */ + public AxisAngle4f rotate(float ang) { + angle += ang; + angle = (float) ((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)); + return this; + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4f}. + * + * @param v + * the vector to transform + * @return v + */ + public Vector3f transform(Vector3f v) { + return transform(v, v); + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4f} + * and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector3f transform(Vector3fc v, Vector3f dest) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + float dot = x * v.x() + y * v.y() + z * v.z(); + dest.set((float) (v.x() * cos + sin * (y * v.z() - z * v.y()) + (1.0 - cos) * dot * x), + (float) (v.y() * cos + sin * (z * v.x() - x * v.z()) + (1.0 - cos) * dot * y), + (float) (v.z() * cos + sin * (x * v.y() - y * v.x()) + (1.0 - cos) * dot * z)); + return dest; + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4f}. + * + * @param v + * the vector to transform + * @return v + */ + public Vector4f transform(Vector4f v) { + return transform(v, v); + } + + /** + * Transform the given vector by the rotation transformation described by this {@link AxisAngle4f} + * and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector4f transform(Vector4fc v, Vector4f dest) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + float dot = x * v.x() + y * v.y() + z * v.z(); + dest.set((float) (v.x() * cos + sin * (y * v.z() - z * v.y()) + (1.0 - cos) * dot * x), + (float) (v.y() * cos + sin * (z * v.x() - x * v.z()) + (1.0 - cos) * dot * y), + (float) (v.z() * cos + sin * (x * v.y() - y * v.x()) + (1.0 - cos) * dot * z), + dest.w); + return dest; + } + + /** + * Return a string representation of this {@link AxisAngle4f}. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string " 0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this {@link AxisAngle4f} by formatting the components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + " " + Runtime.format(z, formatter) + " <| " + Runtime.format(angle, formatter) + ")"; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + float nangle = (float) ((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)); + result = prime * result + Float.floatToIntBits(nangle); + result = prime * result + Float.floatToIntBits(x); + result = prime * result + Float.floatToIntBits(y); + result = prime * result + Float.floatToIntBits(z); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AxisAngle4f other = (AxisAngle4f) obj; + float nangle = (float) ((angle < 0.0 ? Math.PI + Math.PI + angle % (Math.PI + Math.PI) : angle) % (Math.PI + Math.PI)); + float nangleOther = (float) ((other.angle < 0.0 ? Math.PI + Math.PI + other.angle % (Math.PI + Math.PI) : other.angle) % (Math.PI + Math.PI)); + if (Float.floatToIntBits(nangle) != Float.floatToIntBits(nangleOther)) + return false; + if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) + return false; + if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) + return false; + if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z)) + return false; + return true; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/ConfigurationException.java b/src/main/java/com/jozufozu/flywheel/repack/joml/ConfigurationException.java new file mode 100644 index 000000000..e1e41d0c6 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/ConfigurationException.java @@ -0,0 +1,36 @@ +/* + * The MIT License + * + * Copyright (c) 2020-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Exception thrown when using an invalid JOML runtime configuration. + * + * @author Kai Burjack + */ +public class ConfigurationException extends RuntimeException { + private static final long serialVersionUID = -7832356906364070687L; + public ConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/FrustumIntersection.java b/src/main/java/com/jozufozu/flywheel/repack/joml/FrustumIntersection.java new file mode 100644 index 000000000..66fcf18fb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/FrustumIntersection.java @@ -0,0 +1,971 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Efficiently performs frustum intersection tests by caching the frustum planes of an arbitrary transformation {@link Matrix4fc matrix}. + *

+ * This class is preferred over the frustum intersection methods in {@link Matrix4fc} when many objects need to be culled by the same static frustum. + * + * @author Kai Burjack + */ +public class FrustumIntersection { + + /** + * Return value of {@link #intersectAab(float, float, float, float, float, float) intersectAab()} + * and its different overloads identifying the plane with equation x=-1 when using the identity frustum. + */ + public static final int PLANE_NX = 0; + /** + * Return value of {@link #intersectAab(float, float, float, float, float, float) intersectAab()} + * and its different overloads identifying the plane with equation x=1 when using the identity frustum. + */ + public static final int PLANE_PX = 1; + /** + * Return value of {@link #intersectAab(float, float, float, float, float, float) intersectAab()} + * and its different overloads identifying the plane with equation y=-1 when using the identity frustum. + */ + public static final int PLANE_NY= 2; + /** + * Return value of {@link #intersectAab(float, float, float, float, float, float) intersectAab()} + * and its different overloads identifying the plane with equation y=1 when using the identity frustum. + */ + public static final int PLANE_PY = 3; + /** + * Return value of {@link #intersectAab(float, float, float, float, float, float) intersectAab()} + * and its different overloads identifying the plane with equation z=-1 when using the identity frustum. + */ + public static final int PLANE_NZ = 4; + /** + * Return value of {@link #intersectAab(float, float, float, float, float, float) intersectAab()} + * and its different overloads identifying the plane with equation z=1 when using the identity frustum. + */ + public static final int PLANE_PZ = 5; + + /** + * Return value of {@link #intersectAab(float, float, float, float, float, float) intersectAab()} + * and its different overloads indicating that the axis-aligned box intersects the frustum. + */ + public static final int INTERSECT = -1; + /** + * Return value of {@link #intersectAab(float, float, float, float, float, float) intersectAab()} + * and its different overloads indicating that the axis-aligned box is fully inside of the frustum. + */ + public static final int INSIDE = -2; + /** + * Return value of {@link #intersectSphere(Vector3fc, float)} or {@link #intersectSphere(float, float, float, float)} + * indicating that the sphere is completely outside of the frustum. + */ + public static final int OUTSIDE = -3; + + /** + * The value in a bitmask for + * {@link #intersectAab(float, float, float, float, float, float, int) intersectAab()} + * that identifies the plane with equation x=-1 when using the identity frustum. + */ + public static final int PLANE_MASK_NX = 1<x=1 when using the identity frustum. + */ + public static final int PLANE_MASK_PX = 1<y=-1 when using the identity frustum. + */ + public static final int PLANE_MASK_NY = 1<y=1 when using the identity frustum. + */ + public static final int PLANE_MASK_PY = 1<z=-1 when using the identity frustum. + */ + public static final int PLANE_MASK_NZ = 1<z=1 when using the identity frustum. + */ + public static final int PLANE_MASK_PZ = 1< + * Before using any of the frustum culling methods, make sure to define the frustum planes using {@link #set(Matrix4fc)}. + */ + public FrustumIntersection() { + } + + /** + * Create a new {@link FrustumIntersection} from the given {@link Matrix4fc matrix} by extracing the matrix's frustum planes. + *

+ * In order to update the compute frustum planes later on, call {@link #set(Matrix4fc)}. + * + * @see #set(Matrix4fc) + * + * @param m + * the {@link Matrix4fc} to create the frustum culler from + */ + public FrustumIntersection(Matrix4fc m) { + set(m, true); + } + + /** + * Create a new {@link FrustumIntersection} from the given {@link Matrix4fc matrix} by extracing the matrix's frustum planes. + *

+ * In order to update the compute frustum planes later on, call {@link #set(Matrix4fc)}. + * + * @see #set(Matrix4fc) + * + * @param m + * the {@link Matrix4fc} to create the frustum culler from + * @param allowTestSpheres + * whether the methods {@link #testSphere(Vector3fc, float)}, {@link #testSphere(float, float, float, float)}, + * {@link #intersectSphere(Vector3fc, float)} or {@link #intersectSphere(float, float, float, float)} will used. + * If no spheres need to be tested, then false should be used + */ + public FrustumIntersection(Matrix4fc m, boolean allowTestSpheres) { + set(m, allowTestSpheres); + } + + /** + * Update the stored frustum planes of this {@link FrustumIntersection} with the given {@link Matrix4fc matrix}. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param m + * the {@link Matrix4fc matrix} to update this frustum culler's frustum planes from + * @return this + */ + public FrustumIntersection set(Matrix4fc m) { + return set(m, true); + } + + /** + * Update the stored frustum planes of this {@link FrustumIntersection} with the given {@link Matrix4fc matrix} and + * allow to optimize the frustum plane extraction in the case when no intersection test is needed for spheres. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param m + * the {@link Matrix4fc matrix} to update this frustum culler's frustum planes from + * @param allowTestSpheres + * whether the methods {@link #testSphere(Vector3fc, float)}, {@link #testSphere(float, float, float, float)}, + * {@link #intersectSphere(Vector3fc, float)} or {@link #intersectSphere(float, float, float, float)} will be used. + * If no spheres need to be tested, then false should be used + * @return this + */ + public FrustumIntersection set(Matrix4fc m, boolean allowTestSpheres) { + float invl; + nxX = m.m03() + m.m00(); nxY = m.m13() + m.m10(); nxZ = m.m23() + m.m20(); nxW = m.m33() + m.m30(); + if (allowTestSpheres) { + invl = Math.invsqrt(nxX * nxX + nxY * nxY + nxZ * nxZ); + nxX *= invl; nxY *= invl; nxZ *= invl; nxW *= invl; + } + planes[0].set(nxX, nxY, nxZ, nxW); + pxX = m.m03() - m.m00(); pxY = m.m13() - m.m10(); pxZ = m.m23() - m.m20(); pxW = m.m33() - m.m30(); + if (allowTestSpheres) { + invl = Math.invsqrt(pxX * pxX + pxY * pxY + pxZ * pxZ); + pxX *= invl; pxY *= invl; pxZ *= invl; pxW *= invl; + } + planes[1].set(pxX, pxY, pxZ, pxW); + nyX = m.m03() + m.m01(); nyY = m.m13() + m.m11(); nyZ = m.m23() + m.m21(); nyW = m.m33() + m.m31(); + if (allowTestSpheres) { + invl = Math.invsqrt(nyX * nyX + nyY * nyY + nyZ * nyZ); + nyX *= invl; nyY *= invl; nyZ *= invl; nyW *= invl; + } + planes[2].set(nyX, nyY, nyZ, nyW); + pyX = m.m03() - m.m01(); pyY = m.m13() - m.m11(); pyZ = m.m23() - m.m21(); pyW = m.m33() - m.m31(); + if (allowTestSpheres) { + invl = Math.invsqrt(pyX * pyX + pyY * pyY + pyZ * pyZ); + pyX *= invl; pyY *= invl; pyZ *= invl; pyW *= invl; + } + planes[3].set(pyX, pyY, pyZ, pyW); + nzX = m.m03() + m.m02(); nzY = m.m13() + m.m12(); nzZ = m.m23() + m.m22(); nzW = m.m33() + m.m32(); + if (allowTestSpheres) { + invl = Math.invsqrt(nzX * nzX + nzY * nzY + nzZ * nzZ); + nzX *= invl; nzY *= invl; nzZ *= invl; nzW *= invl; + } + planes[4].set(nzX, nzY, nzZ, nzW); + pzX = m.m03() - m.m02(); pzY = m.m13() - m.m12(); pzZ = m.m23() - m.m22(); pzW = m.m33() - m.m32(); + if (allowTestSpheres) { + invl = Math.invsqrt(pzX * pzX + pzY * pzY + pzZ * pzZ); + pzX *= invl; pzY *= invl; pzZ *= invl; pzW *= invl; + } + planes[5].set(pzX, pzY, pzZ, pzW); + return this; + } + + /** + * Test whether the given point is within the frustum defined by this frustum culler. + * + * @param point + * the point to test + * @return true if the given point is inside the frustum; false otherwise + */ + public boolean testPoint(Vector3fc point) { + return testPoint(point.x(), point.y(), point.z()); + } + + /** + * Test whether the given point (x, y, z) is within the frustum defined by this frustum culler. + * + * @param x + * the x-coordinate of the point + * @param y + * the y-coordinate of the point + * @param z + * the z-coordinate of the point + * @return true if the given point is inside the frustum; false otherwise + */ + public boolean testPoint(float x, float y, float z) { + return nxX * x + nxY * y + nxZ * z + nxW >= 0 && + pxX * x + pxY * y + pxZ * z + pxW >= 0 && + nyX * x + nyY * y + nyZ * z + nyW >= 0 && + pyX * x + pyY * y + pyZ * z + pyW >= 0 && + nzX * x + nzY * y + nzZ * z + nzW >= 0 && + pzX * x + pzY * y + pzZ * z + pzW >= 0; + } + + /** + * Test whether the given sphere is partly or completely within or outside of the frustum defined by this frustum culler. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for spheres that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param center + * the sphere's center + * @param radius + * the sphere's radius + * @return true if the given sphere is partly or completely inside the frustum; + * false otherwise + */ + public boolean testSphere(Vector3fc center, float radius) { + return testSphere(center.x(), center.y(), center.z(), radius); + } + + /** + * Test whether the given sphere is partly or completely within or outside of the frustum defined by this frustum culler. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for spheres that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param x + * the x-coordinate of the sphere's center + * @param y + * the y-coordinate of the sphere's center + * @param z + * the z-coordinate of the sphere's center + * @param r + * the sphere's radius + * @return true if the given sphere is partly or completely inside the frustum; + * false otherwise + */ + public boolean testSphere(float x, float y, float z, float r) { + return nxX * x + nxY * y + nxZ * z + nxW >= -r && + pxX * x + pxY * y + pxZ * z + pxW >= -r && + nyX * x + nyY * y + nyZ * z + nyW >= -r && + pyX * x + pyY * y + pyZ * z + pyW >= -r && + nzX * x + nzY * y + nzZ * z + nzW >= -r && + pzX * x + pzY * y + pzZ * z + pzW >= -r; + } + + /** + * Determine whether the given sphere is partly or completely within or outside of the frustum defined by this frustum culler. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns {@link #INTERSECT} for spheres that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param center + * the sphere's center + * @param radius + * the sphere's radius + * @return {@link #INSIDE} if the given sphere is completely inside the frustum, or {@link #INTERSECT} if the sphere intersects + * the frustum, or {@link #OUTSIDE} if the sphere is outside of the frustum + */ + public int intersectSphere(Vector3fc center, float radius) { + return intersectSphere(center.x(), center.y(), center.z(), radius); + } + + /** + * Determine whether the given sphere is partly or completely within or outside of the frustum defined by this frustum culler. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns {@link #INTERSECT} for spheres that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param x + * the x-coordinate of the sphere's center + * @param y + * the y-coordinate of the sphere's center + * @param z + * the z-coordinate of the sphere's center + * @param r + * the sphere's radius + * @return {@link #INSIDE} if the given sphere is completely inside the frustum, or {@link #INTERSECT} if the sphere intersects + * the frustum, or {@link #OUTSIDE} if the sphere is outside of the frustum + */ + public int intersectSphere(float x, float y, float z, float r) { + boolean inside = true; + float dist; + dist = nxX * x + nxY * y + nxZ * z + nxW; + if (dist >= -r) { + inside &= dist >= r; + dist = pxX * x + pxY * y + pxZ * z + pxW; + if (dist >= -r) { + inside &= dist >= r; + dist = nyX * x + nyY * y + nyZ * z + nyW; + if (dist >= -r) { + inside &= dist >= r; + dist = pyX * x + pyY * y + pyZ * z + pyW; + if (dist >= -r) { + inside &= dist >= r; + dist = nzX * x + nzY * y + nzZ * z + nzW; + if (dist >= -r) { + inside &= dist >= r; + dist = pzX * x + pzY * y + pzZ * z + pzW; + if (dist >= -r) { + inside &= dist >= r; + return inside ? INSIDE : INTERSECT; + } + } + } + } + } + } + return OUTSIDE; + } + + /** + * Test whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this frustum culler. + * The box is specified via its min and max corner coordinates. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for boxes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param min + * the minimum corner coordinates of the axis-aligned box + * @param max + * the maximum corner coordinates of the axis-aligned box + * @return true if the axis-aligned box is completely or partly inside of the frustum; false otherwise + */ + public boolean testAab(Vector3fc min, Vector3fc max) { + return testAab(min.x(), min.y(), min.z(), max.x(), max.y(), max.z()); + } + + /** + * Test whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this frustum culler. + * The box is specified via its min and max corner coordinates. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for boxes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: Efficient View Frustum Culling + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param minZ + * the z-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @param maxZ + * the z-coordinate of the maximum corner + * @return true if the axis-aligned box is completely or partly inside of the frustum; false otherwise + */ + public boolean testAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + /* + * This is an implementation of the "2.4 Basic intersection test" of the mentioned site. + * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted. + */ + return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW && + pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW && + nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW && + pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW && + nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW && + pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW; + } + + /** + * Test whether the given XY-plane (at Z = 0) is partly or completely within or outside of the frustum defined by this frustum culler. + * The plane is specified via its min and max corner coordinates. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for planes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param min + * the minimum corner coordinates of the XY-plane + * @param max + * the maximum corner coordinates of the XY-plane + * @return true if the XY-plane is completely or partly inside of the frustum; false otherwise + */ + public boolean testPlaneXY(Vector2fc min, Vector2fc max) { + return testPlaneXY(min.x(), min.y(), max.x(), max.y()); + } + + /** + * Test whether the given XY-plane (at Z = 0) is partly or completely within or outside of the frustum defined by this frustum culler. + * The plane is specified via its min and max corner coordinates. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for planes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: Efficient View Frustum Culling + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @return true if the XY-plane is completely or partly inside of the frustum; false otherwise + */ + public boolean testPlaneXY(float minX, float minY, float maxX, float maxY) { + /* + * This is an implementation of the "2.4 Basic intersection test" of the mentioned site. + * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted. + */ + return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) >= -nxW && + pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) >= -pxW && + nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) >= -nyW && + pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) >= -pyW && + nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) >= -nzW && + pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) >= -pzW; + } + + /** + * Test whether the given XZ-plane (at Y = 0) is partly or completely within or outside of the frustum defined by this frustum culler. + * The plane is specified via its min and max corner coordinates. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for planes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: Efficient View Frustum Culling + * + * @param minX + * the x-coordinate of the minimum corner + * @param minZ + * the z-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxZ + * the z-coordinate of the maximum corner + * @return true if the XZ-plane is completely or partly inside of the frustum; false otherwise + */ + public boolean testPlaneXZ(float minX, float minZ, float maxX, float maxZ) { + /* + * This is an implementation of the "2.4 Basic intersection test" of the mentioned site. + * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted. + */ + return nxX * (nxX < 0 ? minX : maxX) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW && + pxX * (pxX < 0 ? minX : maxX) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW && + nyX * (nyX < 0 ? minX : maxX) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW && + pyX * (pyX < 0 ? minX : maxX) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW && + nzX * (nzX < 0 ? minX : maxX) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW && + pzX * (pzX < 0 ? minX : maxX) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW; + } + + /** + * Determine whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this frustum culler + * and, if the box is not inside this frustum, return the index of the plane that culled it. + * The box is specified via its min and max corner coordinates. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns {@link #INTERSECT} for boxes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param min + * the minimum corner coordinates of the axis-aligned box + * @param max + * the maximum corner coordinates of the axis-aligned box + * @return the index of the first plane that culled the box, if the box does not intersect the frustum; + * or {@link #INTERSECT} if the box intersects the frustum, or {@link #INSIDE} if the box is fully inside of the frustum. + * The plane index is one of {@link #PLANE_NX}, {@link #PLANE_PX}, {@link #PLANE_NY}, {@link #PLANE_PY}, {@link #PLANE_NZ} and {@link #PLANE_PZ} + */ + public int intersectAab(Vector3fc min, Vector3fc max) { + return intersectAab(min.x(), min.y(), min.z(), max.x(), max.y(), max.z()); + } + + /** + * Determine whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this frustum culler + * and, if the box is not inside this frustum, return the index of the plane that culled it. + * The box is specified via its min and max corner coordinates. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns {@link #INTERSECT} for boxes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: Efficient View Frustum Culling + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param minZ + * the z-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @param maxZ + * the z-coordinate of the maximum corner + * @return the index of the first plane that culled the box, if the box does not intersect the frustum, + * or {@link #INTERSECT} if the box intersects the frustum, or {@link #INSIDE} if the box is fully inside of the frustum. + * The plane index is one of {@link #PLANE_NX}, {@link #PLANE_PX}, {@link #PLANE_NY}, {@link #PLANE_PY}, {@link #PLANE_NZ} and {@link #PLANE_PZ} + */ + public int intersectAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + /* + * This is an implementation of the "2.4 Basic intersection test" of the mentioned site. + * + * In addition to the algorithm in the paper, this method also returns the index of the first plane that culled the box. + */ + int plane = PLANE_NX; + boolean inside = true; + if (nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW) { + plane = PLANE_PX; + inside &= nxX * (nxX < 0 ? maxX : minX) + nxY * (nxY < 0 ? maxY : minY) + nxZ * (nxZ < 0 ? maxZ : minZ) >= -nxW; + if (pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW) { + plane = PLANE_NY; + inside &= pxX * (pxX < 0 ? maxX : minX) + pxY * (pxY < 0 ? maxY : minY) + pxZ * (pxZ < 0 ? maxZ : minZ) >= -pxW; + if (nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW) { + plane = PLANE_PY; + inside &= nyX * (nyX < 0 ? maxX : minX) + nyY * (nyY < 0 ? maxY : minY) + nyZ * (nyZ < 0 ? maxZ : minZ) >= -nyW; + if (pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW) { + plane = PLANE_NZ; + inside &= pyX * (pyX < 0 ? maxX : minX) + pyY * (pyY < 0 ? maxY : minY) + pyZ * (pyZ < 0 ? maxZ : minZ) >= -pyW; + if (nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW) { + plane = PLANE_PZ; + inside &= nzX * (nzX < 0 ? maxX : minX) + nzY * (nzY < 0 ? maxY : minY) + nzZ * (nzZ < 0 ? maxZ : minZ) >= -nzW; + if (pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW) { + inside &= pzX * (pzX < 0 ? maxX : minX) + pzY * (pzY < 0 ? maxY : minY) + pzZ * (pzZ < 0 ? maxZ : minZ) >= -pzW; + return inside ? INSIDE : INTERSECT; + } + } + } + } + } + } + return plane; + } + + /** + * Compute the signed distance from the given axis-aligned box to the plane. + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param minZ + * the z-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @param maxZ + * the z-coordinate of the maximum corner + * @param plane + * one of + * {@link #PLANE_NX}, {@link #PLANE_PX}, + * {@link #PLANE_NY}, {@link #PLANE_PY}, + * {@link #PLANE_NZ} and {@link #PLANE_PZ} + * @return the signed distance of the axis-aligned box to the plane + */ + public float distanceToPlane(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int plane) { + return planes[plane].x * (planes[plane].x < 0 ? maxX : minX) + planes[plane].y * (planes[plane].y < 0 ? maxY : minY) + + planes[plane].z * (planes[plane].z < 0 ? maxZ : minZ) + planes[plane].w; + } + + /** + * Determine whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this frustum culler + * and, if the box is not inside this frustum, return the index of the plane that culled it. + * The box is specified via its min and max corner coordinates. + *

+ * This method differs from {@link #intersectAab(Vector3fc, Vector3fc)} in that + * it allows to mask-off planes that should not be calculated. For example, in order to only test a box against the + * left frustum plane, use a mask of {@link #PLANE_MASK_NX}. Or in order to test all planes except the left plane, use + * a mask of (~0 ^ PLANE_MASK_NX). + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns {@link #INTERSECT} for boxes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param min + * the minimum corner coordinates of the axis-aligned box + * @param max + * the maximum corner coordinates of the axis-aligned box + * @param mask + * contains as bitset all the planes that should be tested. + * This value can be any combination of + * {@link #PLANE_MASK_NX}, {@link #PLANE_MASK_PX}, + * {@link #PLANE_MASK_NY}, {@link #PLANE_MASK_PY}, + * {@link #PLANE_MASK_NZ} and {@link #PLANE_MASK_PZ} + * @return the index of the first plane that culled the box, if the box does not intersect the frustum, + * or {@link #INTERSECT} if the box intersects the frustum, or {@link #INSIDE} if the box is fully inside of the frustum. + * The plane index is one of {@link #PLANE_NX}, {@link #PLANE_PX}, {@link #PLANE_NY}, {@link #PLANE_PY}, {@link #PLANE_NZ} and {@link #PLANE_PZ} + */ + public int intersectAab(Vector3fc min, Vector3fc max, int mask) { + return intersectAab(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), mask); + } + + /** + * Determine whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this frustum culler + * and, if the box is not inside this frustum, return the index of the plane that culled it. + * The box is specified via its min and max corner coordinates. + *

+ * This method differs from {@link #intersectAab(float, float, float, float, float, float)} in that + * it allows to mask-off planes that should not be calculated. For example, in order to only test a box against the + * left frustum plane, use a mask of {@link #PLANE_MASK_NX}. Or in order to test all planes except the left plane, use + * a mask of (~0 ^ PLANE_MASK_NX). + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns {@link #INTERSECT} for boxes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: Efficient View Frustum Culling + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param minZ + * the z-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @param maxZ + * the z-coordinate of the maximum corner + * @param mask + * contains as bitset all the planes that should be tested. + * This value can be any combination of + * {@link #PLANE_MASK_NX}, {@link #PLANE_MASK_PX}, + * {@link #PLANE_MASK_NY}, {@link #PLANE_MASK_PY}, + * {@link #PLANE_MASK_NZ} and {@link #PLANE_MASK_PZ} + * @return the index of the first plane that culled the box, if the box does not intersect the frustum, + * or {@link #INTERSECT} if the box intersects the frustum, or {@link #INSIDE} if the box is fully inside of the frustum. + * The plane index is one of {@link #PLANE_NX}, {@link #PLANE_PX}, {@link #PLANE_NY}, {@link #PLANE_PY}, {@link #PLANE_NZ} and {@link #PLANE_PZ} + */ + public int intersectAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int mask) { + /* + * This is an implementation of the first algorithm in "2.5 Plane masking and coherency" of the mentioned site. + * + * In addition to the algorithm in the paper, this method also returns the index of the first plane that culled the box. + */ + int plane = PLANE_NX; + boolean inside = true; + if ((mask & PLANE_MASK_NX) == 0 || nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW) { + plane = PLANE_PX; + inside &= nxX * (nxX < 0 ? maxX : minX) + nxY * (nxY < 0 ? maxY : minY) + nxZ * (nxZ < 0 ? maxZ : minZ) >= -nxW; + if ((mask & PLANE_MASK_PX) == 0 || pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW) { + plane = PLANE_NY; + inside &= pxX * (pxX < 0 ? maxX : minX) + pxY * (pxY < 0 ? maxY : minY) + pxZ * (pxZ < 0 ? maxZ : minZ) >= -pxW; + if ((mask & PLANE_MASK_NY) == 0 || nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW) { + plane = PLANE_PY; + inside &= nyX * (nyX < 0 ? maxX : minX) + nyY * (nyY < 0 ? maxY : minY) + nyZ * (nyZ < 0 ? maxZ : minZ) >= -nyW; + if ((mask & PLANE_MASK_PY) == 0 || pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW) { + plane = PLANE_NZ; + inside &= pyX * (pyX < 0 ? maxX : minX) + pyY * (pyY < 0 ? maxY : minY) + pyZ * (pyZ < 0 ? maxZ : minZ) >= -pyW; + if ((mask & PLANE_MASK_NZ) == 0 || nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW) { + plane = PLANE_PZ; + inside &= nzX * (nzX < 0 ? maxX : minX) + nzY * (nzY < 0 ? maxY : minY) + nzZ * (nzZ < 0 ? maxZ : minZ) >= -nzW; + if ((mask & PLANE_MASK_PZ) == 0 || pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW) { + inside &= pzX * (pzX < 0 ? maxX : minX) + pzY * (pzY < 0 ? maxY : minY) + pzZ * (pzZ < 0 ? maxZ : minZ) >= -pzW; + return inside ? INSIDE : INTERSECT; + } + } + } + } + } + } + return plane; + } + + /** + * Determine whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this frustum culler + * and, if the box is not inside this frustum, return the index of the plane that culled it. + * The box is specified via its min and max corner coordinates. + *

+ * This method differs from {@link #intersectAab(Vector3fc, Vector3fc)} in that + * it allows to mask-off planes that should not be calculated. For example, in order to only test a box against the + * left frustum plane, use a mask of {@link #PLANE_MASK_NX}. Or in order to test all planes except the left plane, use + * a mask of (~0 ^ PLANE_MASK_NX). + *

+ * In addition, the startPlane denotes the first frustum plane to test the box against. To use this effectively means to store the + * plane that previously culled an axis-aligned box (as returned by intersectAab()) and in the next frame use the return value + * as the argument to the startPlane parameter of this method. The assumption is that the plane that culled the object previously will also + * cull it now (temporal coherency) and the culling computation is likely reduced in that case. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns {@link #INTERSECT} for boxes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + * + * @param min + * the minimum corner coordinates of the axis-aligned box + * @param max + * the maximum corner coordinates of the axis-aligned box + * @param mask + * contains as bitset all the planes that should be tested. + * This value can be any combination of + * {@link #PLANE_MASK_NX}, {@link #PLANE_MASK_PX}, + * {@link #PLANE_MASK_NY}, {@link #PLANE_MASK_PY}, + * {@link #PLANE_MASK_NZ} and {@link #PLANE_MASK_PZ} + * @param startPlane + * the first frustum plane to test the axis-aligned box against. It is one of + * {@link #PLANE_NX}, {@link #PLANE_PX}, {@link #PLANE_NY}, {@link #PLANE_PY}, {@link #PLANE_NZ} and {@link #PLANE_PZ} + * @return the index of the first plane that culled the box, if the box does not intersect the frustum, + * or {@link #INTERSECT} if the box intersects the frustum, or {@link #INSIDE} if the box is fully inside of the frustum. + * The plane index is one of {@link #PLANE_NX}, {@link #PLANE_PX}, {@link #PLANE_NY}, {@link #PLANE_PY}, {@link #PLANE_NZ} and {@link #PLANE_PZ} + */ + public int intersectAab(Vector3fc min, Vector3fc max, int mask, int startPlane) { + return intersectAab(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), mask, startPlane); + } + + /** + * Determine whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this frustum culler + * and, if the box is not inside this frustum, return the index of the plane that culled it. + * The box is specified via its min and max corner coordinates. + *

+ * This method differs from {@link #intersectAab(float, float, float, float, float, float)} in that + * it allows to mask-off planes that should not be calculated. For example, in order to only test a box against the + * left frustum plane, use a mask of {@link #PLANE_MASK_NX}. Or in order to test all planes except the left plane, use + * a mask of (~0 ^ PLANE_MASK_NX). + *

+ * In addition, the startPlane denotes the first frustum plane to test the box against. To use this effectively means to store the + * plane that previously culled an axis-aligned box (as returned by intersectAab()) and in the next frame use the return value + * as the argument to the startPlane parameter of this method. The assumption is that the plane that culled the object previously will also + * cull it now (temporal coherency) and the culling computation is likely reduced in that case. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns {@link #INTERSECT} for boxes that do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: Efficient View Frustum Culling + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param minZ + * the z-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @param maxZ + * the z-coordinate of the maximum corner + * @param mask + * contains as bitset all the planes that should be tested. + * This value can be any combination of + * {@link #PLANE_MASK_NX}, {@link #PLANE_MASK_PX}, + * {@link #PLANE_MASK_NY}, {@link #PLANE_MASK_PY}, + * {@link #PLANE_MASK_NZ} and {@link #PLANE_MASK_PZ} + * @param startPlane + * the first frustum plane to test the axis-aligned box against. It is one of + * {@link #PLANE_NX}, {@link #PLANE_PX}, {@link #PLANE_NY}, {@link #PLANE_PY}, {@link #PLANE_NZ} and {@link #PLANE_PZ} + * @return the index of the first plane that culled the box, if the box does not intersect the frustum, + * or {@link #INTERSECT} if the box intersects the frustum, or {@link #INSIDE} if the box is fully inside of the frustum. + * The plane index is one of {@link #PLANE_NX}, {@link #PLANE_PX}, {@link #PLANE_NY}, {@link #PLANE_PY}, {@link #PLANE_NZ} and {@link #PLANE_PZ} + */ + public int intersectAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int mask, int startPlane) { + /* + * This is an implementation of the second algorithm in "2.5 Plane masking and coherency" of the mentioned site. + * + * In addition to the algorithm in the paper, this method also returns the index of the first plane that culled the box. + */ + int plane = startPlane; + boolean inside = true; + Vector4f p = planes[startPlane]; + if ((mask & 1<= -nxW) { + plane = PLANE_PX; + inside &= nxX * (nxX < 0 ? maxX : minX) + nxY * (nxY < 0 ? maxY : minY) + nxZ * (nxZ < 0 ? maxZ : minZ) >= -nxW; + if ((mask & PLANE_MASK_PX) == 0 || pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW) { + plane = PLANE_NY; + inside &= pxX * (pxX < 0 ? maxX : minX) + pxY * (pxY < 0 ? maxY : minY) + pxZ * (pxZ < 0 ? maxZ : minZ) >= -pxW; + if ((mask & PLANE_MASK_NY) == 0 || nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW) { + plane = PLANE_PY; + inside &= nyX * (nyX < 0 ? maxX : minX) + nyY * (nyY < 0 ? maxY : minY) + nyZ * (nyZ < 0 ? maxZ : minZ) >= -nyW; + if ((mask & PLANE_MASK_PY) == 0 || pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW) { + plane = PLANE_NZ; + inside &= pyX * (pyX < 0 ? maxX : minX) + pyY * (pyY < 0 ? maxY : minY) + pyZ * (pyZ < 0 ? maxZ : minZ) >= -pyW; + if ((mask & PLANE_MASK_NZ) == 0 || nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW) { + plane = PLANE_PZ; + inside &= nzX * (nzX < 0 ? maxX : minX) + nzY * (nzY < 0 ? maxY : minY) + nzZ * (nzZ < 0 ? maxZ : minZ) >= -nzW; + if ((mask & PLANE_MASK_PZ) == 0 || pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW) { + inside &= pzX * (pzX < 0 ? maxX : minX) + pzY * (pzY < 0 ? maxY : minY) + pzZ * (pzZ < 0 ? maxZ : minZ) >= -pzW; + return inside ? INSIDE : INTERSECT; + } + } + } + } + } + } + return plane; + } + + /** + * Test whether the given line segment, defined by the end points a and b, + * is partly or completely within the frustum defined by this frustum culler. + * + * @param a + * the line segment's first end point + * @param b + * the line segment's second end point + * @return true if the given line segment is partly or completely inside the frustum; + * false otherwise + */ + public boolean testLineSegment(Vector3fc a, Vector3fc b) { + return testLineSegment(a.x(), a.y(), a.z(), b.x(), b.y(), b.z()); + } + + /** + * Test whether the given line segment, defined by the end points (aX, aY, aZ) and (bX, bY, bZ), + * is partly or completely within the frustum defined by this frustum culler. + * + * @param aX + * the x coordinate of the line segment's first end point + * @param aY + * the y coordinate of the line segment's first end point + * @param aZ + * the z coordinate of the line segment's first end point + * @param bX + * the x coordinate of the line segment's second end point + * @param bY + * the y coordinate of the line segment's second end point + * @param bZ + * the z coordinate of the line segment's second end point + * @return true if the given line segment is partly or completely inside the frustum; + * false otherwise + */ + public boolean testLineSegment(float aX, float aY, float aZ, float bX, float bY, float bZ) { + float da, db; + da = Math.fma(nxX, aX, Math.fma(nxY, aY, Math.fma(nxZ, aZ, nxW))); + db = Math.fma(nxX, bX, Math.fma(nxY, bY, Math.fma(nxZ, bZ, nxW))); + if (da < 0.0f && db < 0.0f) + return false; + if (da * db < 0.0f) { + float p = Math.abs(da) / Math.abs(db - da); + float dx = Math.fma(bX - aX, p, aX), dy = Math.fma(bY - aY, p, aY), dz = Math.fma(bZ - aZ, p, aZ); + if (da < 0.0f) { + aX = dx; aY = dy; aZ = dz; + } else { + bX = dx; bY = dy; bZ = dz; + } + } + da = Math.fma(pxX, aX, Math.fma(pxY, aY, Math.fma(pxZ, aZ, pxW))); + db = Math.fma(pxX, bX, Math.fma(pxY, bY, Math.fma(pxZ, bZ, pxW))); + if (da < 0.0f && db < 0.0f) + return false; + if (da * db < 0.0f) { + float p = Math.abs(da) / Math.abs(db - da); + float dx = Math.fma(bX - aX, p, aX), dy = Math.fma(bY - aY, p, aY), dz = Math.fma(bZ - aZ, p, aZ); + if (da < 0.0f) { + aX = dx; aY = dy; aZ = dz; + } else { + bX = dx; bY = dy; bZ = dz; + } + } + da = Math.fma(nyX, aX, Math.fma(nyY, aY, Math.fma(nyZ, aZ, nyW))); + db = Math.fma(nyX, bX, Math.fma(nyY, bY, Math.fma(nyZ, bZ, nyW))); + if (da < 0.0f && db < 0.0f) + return false; + if (da * db < 0.0f) { + float p = Math.abs(da) / Math.abs(db - da); + float dx = Math.fma(bX - aX, p, aX), dy = Math.fma(bY - aY, p, aY), dz = Math.fma(bZ - aZ, p, aZ); + if (da < 0.0f) { + aX = dx; aY = dy; aZ = dz; + } else { + bX = dx; bY = dy; bZ = dz; + } + } + da = Math.fma(pyX, aX, Math.fma(pyY, aY, Math.fma(pyZ, aZ, pyW))); + db = Math.fma(pyX, bX, Math.fma(pyY, bY, Math.fma(pyZ, bZ, pyW))); + if (da < 0.0f && db < 0.0f) + return false; + if (da * db < 0.0f) { + float p = Math.abs(da) / Math.abs(db - da); + float dx = Math.fma(bX - aX, p, aX), dy = Math.fma(bY - aY, p, aY), dz = Math.fma(bZ - aZ, p, aZ); + if (da < 0.0f) { + aX = dx; aY = dy; aZ = dz; + } else { + bX = dx; bY = dy; bZ = dz; + } + } + da = Math.fma(nzX, aX, Math.fma(nzY, aY, Math.fma(nzZ, aZ, nzW))); + db = Math.fma(nzX, bX, Math.fma(nzY, bY, Math.fma(nzZ, bZ, nzW))); + if (da < 0.0f && db < 0.0f) + return false; + if (da * db < 0.0f) { + float p = Math.abs(da) / Math.abs(db - da); + float dx = Math.fma(bX - aX, p, aX), dy = Math.fma(bY - aY, p, aY), dz = Math.fma(bZ - aZ, p, aZ); + if (da < 0.0f) { + aX = dx; aY = dy; aZ = dz; + } else { + bX = dx; bY = dy; bZ = dz; + } + } + da = Math.fma(pzX, aX, Math.fma(pzY, aY, Math.fma(pzZ, aZ, pzW))); + db = Math.fma(pzX, bX, Math.fma(pzY, bY, Math.fma(pzZ, bZ, pzW))); + return da >= 0.0f || db >= 0.0f; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/FrustumRayBuilder.java b/src/main/java/com/jozufozu/flywheel/repack/joml/FrustumRayBuilder.java new file mode 100644 index 000000000..5520ca304 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/FrustumRayBuilder.java @@ -0,0 +1,154 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Provides methods to compute rays through an arbitrary perspective transformation defined by a {@link Matrix4fc}. + *

+ * This can be used to compute the eye-rays in simple software-based raycasting/raytracing. + *

+ * To obtain the origin of the rays call {@link #origin(Vector3f)}. + * Then to compute the directions of subsequent rays use {@link #dir(float, float, Vector3f)}. + * + * @author Kai Burjack + */ +public class FrustumRayBuilder { + + private float nxnyX, nxnyY, nxnyZ; + private float pxnyX, pxnyY, pxnyZ; + private float pxpyX, pxpyY, pxpyZ; + private float nxpyX, nxpyY, nxpyZ; + private float cx, cy, cz; + + /** + * Create a new {@link FrustumRayBuilder} with an undefined frustum. + *

+ * Before obtaining ray directions, make sure to define the frustum using {@link #set(Matrix4fc)}. + */ + public FrustumRayBuilder() { + } + + /** + * Create a new {@link FrustumRayBuilder} from the given {@link Matrix4fc matrix} by extracing the matrix's frustum. + * + * @param m + * the {@link Matrix4fc} to create the frustum from + */ + public FrustumRayBuilder(Matrix4fc m) { + set(m); + } + + /** + * Update the stored frustum corner rays and origin of this {@link FrustumRayBuilder} with the given {@link Matrix4fc matrix}. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + *

+ * Reference: http://geomalgorithms.com + * + * @param m + * the {@link Matrix4fc matrix} to update the frustum corner rays and origin with + * @return this + */ + public FrustumRayBuilder set(Matrix4fc m) { + float nxX = m.m03() + m.m00(), nxY = m.m13() + m.m10(), nxZ = m.m23() + m.m20(), d1 = m.m33() + m.m30(); + float pxX = m.m03() - m.m00(), pxY = m.m13() - m.m10(), pxZ = m.m23() - m.m20(), d2 = m.m33() - m.m30(); + float nyX = m.m03() + m.m01(), nyY = m.m13() + m.m11(), nyZ = m.m23() + m.m21(); + float pyX = m.m03() - m.m01(), pyY = m.m13() - m.m11(), pyZ = m.m23() - m.m21(), d3 = m.m33() - m.m31(); + // bottom left + nxnyX = nyY * nxZ - nyZ * nxY; + nxnyY = nyZ * nxX - nyX * nxZ; + nxnyZ = nyX * nxY - nyY * nxX; + // bottom right + pxnyX = pxY * nyZ - pxZ * nyY; + pxnyY = pxZ * nyX - pxX * nyZ; + pxnyZ = pxX * nyY - pxY * nyX; + // top left + nxpyX = nxY * pyZ - nxZ * pyY; + nxpyY = nxZ * pyX - nxX * pyZ; + nxpyZ = nxX * pyY - nxY * pyX; + // top right + pxpyX = pyY * pxZ - pyZ * pxY; + pxpyY = pyZ * pxX - pyX * pxZ; + pxpyZ = pyX * pxY - pyY * pxX; + // compute origin + float pxnxX, pxnxY, pxnxZ; + pxnxX = pxY * nxZ - pxZ * nxY; + pxnxY = pxZ * nxX - pxX * nxZ; + pxnxZ = pxX * nxY - pxY * nxX; + float invDot = 1.0f / (nxX * pxpyX + nxY * pxpyY + nxZ * pxpyZ); + cx = (-pxpyX * d1 - nxpyX * d2 - pxnxX * d3) * invDot; + cy = (-pxpyY * d1 - nxpyY * d2 - pxnxY * d3) * invDot; + cz = (-pxpyZ * d1 - nxpyZ * d2 - pxnxZ * d3) * invDot; + return this; + } + + /** + * Store the eye/origin of the perspective frustum in the given origin. + * + * @param origin + * will hold the perspective origin + * @return the origin vector + */ + public Vector3fc origin(Vector3f origin) { + origin.x = cx; + origin.y = cy; + origin.z = cz; + return origin; + } + + /** + * Obtain the normalized direction of a ray starting at the center of the coordinate system and going + * through the near frustum plane. + *

+ * The parameters x and y are used to interpolate the generated ray direction + * from the bottom-left to the top-right frustum corners. + * + * @param x + * the interpolation factor along the left-to-right frustum planes, within [0..1] + * @param y + * the interpolation factor along the bottom-to-top frustum planes, within [0..1] + * @param dir + * will hold the normalized ray direction + * @return the dir vector + */ + public Vector3fc dir(float x, float y, Vector3f dir) { + float y1x = nxnyX + (nxpyX - nxnyX) * y; + float y1y = nxnyY + (nxpyY - nxnyY) * y; + float y1z = nxnyZ + (nxpyZ - nxnyZ) * y; + float y2x = pxnyX + (pxpyX - pxnyX) * y; + float y2y = pxnyY + (pxpyY - pxnyY) * y; + float y2z = pxnyZ + (pxpyZ - pxnyZ) * y; + float dx = y1x + (y2x - y1x) * x; + float dy = y1y + (y2y - y1y) * x; + float dz = y1z + (y2z - y1z) * x; + // normalize the vector + float invLen = Math.invsqrt(dx * dx + dy * dy + dz * dz); + dir.x = dx * invLen; + dir.y = dy * invLen; + dir.z = dz * invLen; + return dir; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/GeometryUtils.java b/src/main/java/com/jozufozu/flywheel/repack/joml/GeometryUtils.java new file mode 100644 index 000000000..88a2c1c11 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/GeometryUtils.java @@ -0,0 +1,247 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Useful geometry methods. + * + * @author Kai Burjack + * @author Richard Greenlees + */ +public class GeometryUtils { + + /** + * Compute two arbitrary vectors perpendicular to the given normalized vector (x, y, z), and store them in dest1 and dest2, + * respectively. + *

+ * The computed vectors will themselves be perpendicular to each another and normalized. So the tree vectors (x, y, z), dest1 and + * dest2 form an orthonormal basis. + * + * @param x + * the x coordinate of the normalized input vector + * @param y + * the y coordinate of the normalized input vector + * @param z + * the z coordinate of the normalized input vector + * @param dest1 + * will hold the first perpendicular vector + * @param dest2 + * will hold the second perpendicular vector + */ + public static void perpendicular(float x, float y, float z, Vector3f dest1, Vector3f dest2) { + float magX = z * z + y * y; + float magY = z * z + x * x; + float magZ = y * y + x * x; + float mag; + if (magX > magY && magX > magZ) { + dest1.x = 0; + dest1.y = z; + dest1.z = -y; + mag = magX; + } else if (magY > magZ) { + dest1.x = -z; + dest1.y = 0; + dest1.z = x; + mag = magY; + } else { + dest1.x = y; + dest1.y = -x; + dest1.z = 0; + mag = magZ; + } + float len = Math.invsqrt(mag); + dest1.x *= len; + dest1.y *= len; + dest1.z *= len; + dest2.x = y * dest1.z - z * dest1.y; + dest2.y = z * dest1.x - x * dest1.z; + dest2.z = x * dest1.y - y * dest1.x; + } + + /** + * Compute two arbitrary vectors perpendicular to the given normalized vector v, and store them in dest1 and dest2, + * respectively. + *

+ * The computed vectors will themselves be perpendicular to each another and normalized. So the tree vectors v, dest1 and + * dest2 form an orthonormal basis. + * + * @param v + * the {@link Vector3f#normalize() normalized} input vector + * @param dest1 + * will hold the first perpendicular vector + * @param dest2 + * will hold the second perpendicular vector + */ + public static void perpendicular(Vector3fc v, Vector3f dest1, Vector3f dest2) { + perpendicular(v.x(), v.y(), v.z(), dest1, dest2); + } + + /** + * Calculate the normal of a surface defined by points v1, v2 and v3 and store it in dest. + * + * @param v0 + * the first position + * @param v1 + * the second position + * @param v2 + * the third position + * @param dest + * will hold the result + */ + public static void normal(Vector3fc v0, Vector3fc v1, Vector3fc v2, Vector3f dest) { + normal(v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), dest); + } + + /** + * Calculate the normal of a surface defined by points (v1X, v1Y, v1Z), (v2X, v2Y, v2Z) and (v3X, v3Y, v3Z) + * and store it in dest. + * + * @param v0X + * the x coordinate of the first position + * @param v0Y + * the y coordinate of the first position + * @param v0Z + * the z coordinate of the first position + * @param v1X + * the x coordinate of the second position + * @param v1Y + * the y coordinate of the second position + * @param v1Z + * the z coordinate of the second position + * @param v2X + * the x coordinate of the third position + * @param v2Y + * the y coordinate of the third position + * @param v2Z + * the z coordinate of the third position + * @param dest + * will hold the result + */ + public static void normal(float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, Vector3f dest) { + dest.x = ((v1Y - v0Y) * (v2Z - v0Z)) - ((v1Z - v0Z) * (v2Y - v0Y)); + dest.y = ((v1Z - v0Z) * (v2X - v0X)) - ((v1X - v0X) * (v2Z - v0Z)); + dest.z = ((v1X - v0X) * (v2Y - v0Y)) - ((v1Y - v0Y) * (v2X - v0X)); + dest.normalize(); + } + + /** + * Calculate the surface tangent for the three supplied vertices and UV coordinates and store the result in dest. + * + * @param v1 + * XYZ of first vertex + * @param uv1 + * UV of first vertex + * @param v2 + * XYZ of second vertex + * @param uv2 + * UV of second vertex + * @param v3 + * XYZ of third vertex + * @param uv3 + * UV of third vertex + * @param dest + * the tangent will be stored here + */ + public static void tangent(Vector3fc v1, Vector2fc uv1, Vector3fc v2, Vector2fc uv2, Vector3fc v3, Vector2fc uv3, Vector3f dest) { + float DeltaV1 = uv2.y() - uv1.y(); + float DeltaV2 = uv3.y() - uv1.y(); + + float f = 1.0f / ((uv2.x() - uv1.x()) * DeltaV2 - (uv3.x() - uv1.x()) * DeltaV1); + + dest.x = f * (DeltaV2 * (v2.x() - v1.x()) - DeltaV1 * (v3.x() - v1.x())); + dest.y = f * (DeltaV2 * (v2.y() - v1.y()) - DeltaV1 * (v3.y() - v1.y())); + dest.z = f * (DeltaV2 * (v2.z() - v1.z()) - DeltaV1 * (v3.z() - v1.z())); + dest.normalize(); + } + + /** + * Calculate the surface bitangent for the three supplied vertices and UV coordinates and store the result in dest. + * + * @param v1 + * XYZ of first vertex + * @param uv1 + * UV of first vertex + * @param v2 + * XYZ of second vertex + * @param uv2 + * UV of second vertex + * @param v3 + * XYZ of third vertex + * @param uv3 + * UV of third vertex + * @param dest + * the binormal will be stored here + */ + public static void bitangent(Vector3fc v1, Vector2fc uv1, Vector3fc v2, Vector2fc uv2, Vector3fc v3, Vector2fc uv3, Vector3f dest) { + float DeltaU1 = uv2.x() - uv1.x(); + float DeltaU2 = uv3.x() - uv1.x(); + + float f = 1.0f / (DeltaU1 * (uv3.y() - uv1.y()) - DeltaU2 * (uv2.y() - uv1.y())); + + dest.x = f * (-DeltaU2 * (v2.x() - v1.x()) + DeltaU1 * (v3.x() - v1.x())); + dest.y = f * (-DeltaU2 * (v2.y() - v1.y()) + DeltaU1 * (v3.y() - v1.y())); + dest.z = f * (-DeltaU2 * (v2.z() - v1.z()) + DeltaU1 * (v3.z() - v1.z())); + dest.normalize(); + } + + /** + * Calculate the surface tangent and bitangent for the three supplied vertices and UV coordinates and store the result in dest. + * + * @param v1 + * XYZ of first vertex + * @param uv1 + * UV of first vertex + * @param v2 + * XYZ of second vertex + * @param uv2 + * UV of second vertex + * @param v3 + * XYZ of third vertex + * @param uv3 + * UV of third vertex + * @param destTangent + * the tangent will be stored here + * @param destBitangent + * the bitangent will be stored here + */ + public static void tangentBitangent(Vector3fc v1, Vector2fc uv1, Vector3fc v2, Vector2fc uv2, Vector3fc v3, Vector2fc uv3, Vector3f destTangent, Vector3f destBitangent) { + float DeltaV1 = uv2.y() - uv1.y(); + float DeltaV2 = uv3.y() - uv1.y(); + float DeltaU1 = uv2.x() - uv1.x(); + float DeltaU2 = uv3.x() - uv1.x(); + + float f = 1.0f / (DeltaU1 * DeltaV2 - DeltaU2 * DeltaV1); + + destTangent.x = f * (DeltaV2 * (v2.x() - v1.x()) - DeltaV1 * (v3.x() - v1.x())); + destTangent.y = f * (DeltaV2 * (v2.y() - v1.y()) - DeltaV1 * (v3.y() - v1.y())); + destTangent.z = f * (DeltaV2 * (v2.z() - v1.z()) - DeltaV1 * (v3.z() - v1.z())); + destTangent.normalize(); + + destBitangent.x = f * (-DeltaU2 * (v2.x() - v1.x()) + DeltaU1 * (v3.x() - v1.x())); + destBitangent.y = f * (-DeltaU2 * (v2.y() - v1.y()) + DeltaU1 * (v3.y() - v1.y())); + destBitangent.z = f * (-DeltaU2 * (v2.z() - v1.z()) + DeltaU1 * (v3.z() - v1.z())); + destBitangent.normalize(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Interpolationd.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Interpolationd.java new file mode 100644 index 000000000..bbe11ef61 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Interpolationd.java @@ -0,0 +1,337 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Contains various interpolation functions. + * + * @author Kai Burjack + */ +public class Interpolationd { + + /** + * Bilinearly interpolate the single scalar value f over the given triangle. + *

+ * Reference: https://en.wikipedia.org/ + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0 + * the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1 + * the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2 + * the value of f at the third vertex + * @param x + * the x coordinate of the point to interpolate f at + * @param y + * the y coordinate of the point to interpolate f at + * @return the interpolated value of f + */ + public static double interpolateTriangle( + double v0X, double v0Y, double f0, + double v1X, double v1Y, double f1, + double v2X, double v2Y, double f2, + double x, double y) { + double v12Y = v1Y - v2Y; + double v21X = v2X - v1X; + double v02X = v0X - v2X; + double yv2Y = y - v2Y; + double xv2X = x - v2X; + double v02Y = v0Y - v2Y; + double invDen = 1.0 / (v12Y * v02X + v21X * v02Y); + double l1 = (v12Y * xv2X + v21X * yv2Y) * invDen; + double l2 = (v02X * yv2Y - v02Y * xv2X) * invDen; + return l1 * f0 + l2 * f1 + (1.0f - l1 - l2) * f2; + } + + /** + * Bilinearly interpolate the two-dimensional vector f over the given triangle and store the result in dest. + *

+ * Reference: https://en.wikipedia.org/ + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0X + * the x component of the value of f at the first vertex + * @param f0Y + * the y component of the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1X + * the x component of the value of f at the second vertex + * @param f1Y + * the y component of the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2X + * the x component of the value of f at the third vertex + * @param f2Y + * the y component of the value of f at the third vertex + * @param x + * the x coordinate of the point to interpolate f at + * @param y + * the y coordinate of the point to interpolate f at + * @param dest + * will hold the interpolation result + * @return dest + */ + public static Vector2d interpolateTriangle( + double v0X, double v0Y, double f0X, double f0Y, + double v1X, double v1Y, double f1X, double f1Y, + double v2X, double v2Y, double f2X, double f2Y, + double x, double y, Vector2d dest) { + double v12Y = v1Y - v2Y; + double v21X = v2X - v1X; + double v02X = v0X - v2X; + double yv2Y = y - v2Y; + double xv2X = x - v2X; + double v02Y = v0Y - v2Y; + double invDen = 1.0 / (v12Y * v02X + v21X * v02Y); + double l1 = (v12Y * xv2X + v21X * yv2Y) * invDen; + double l2 = (v02X * yv2Y - v02Y * xv2X) * invDen; + double l3 = 1.0 - l1 - l2; + dest.x = l1 * f0X + l2 * f1X + l3 * f2X; + dest.y = l1 * f0Y + l2 * f1Y + l3 * f2Y; + return dest; + } + + /** + * Compute the first-order derivative of a linear two-dimensional function f with respect to X + * and store the result in dest. + *

+ * This method computes the constant rate of change for f given the three values of f + * at the specified three inputs (v0X, v0Y), (v1X, v1Y) and (v2X, v2Y). + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0X + * the x component of the value of f at the first vertex + * @param f0Y + * the y component of the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1X + * the x component of the value of f at the second vertex + * @param f1Y + * the y component of the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2X + * the x component of the value of f at the third vertex + * @param f2Y + * the y component of the value of f at the third vertex + * @param dest + * will hold the result + * @return dest + */ + public static Vector2d dFdxLinear( + double v0X, double v0Y, double f0X, double f0Y, + double v1X, double v1Y, double f1X, double f1Y, + double v2X, double v2Y, double f2X, double f2Y, Vector2d dest) { + double v12Y = v1Y - v2Y; + double v02Y = v0Y - v2Y; + double den = v12Y * (v0X - v2X) + (v2X - v1X) * v02Y; + double l3_1 = den - v12Y + v02Y; + double invDen = 1.0f / den; + dest.x = invDen * (v12Y * f0X - v02Y * f1X + l3_1 * f2X) - f2X; + dest.y = invDen * (v12Y * f0Y - v02Y * f1Y + l3_1 * f2Y) - f2Y; + return dest; + } + + /** + * Compute the first-order derivative of a linear two-dimensional function f with respect to Y + * and store the result in dest. + *

+ * This method computes the constant rate of change for f given the three values of f + * at the specified three inputs (v0X, v0Y), (v1X, v1Y) and (v2X, v2Y). + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0X + * the x component of the value of f at the first vertex + * @param f0Y + * the y component of the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1X + * the x component of the value of f at the second vertex + * @param f1Y + * the y component of the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2X + * the x component of the value of f at the third vertex + * @param f2Y + * the y component of the value of f at the third vertex + * @param dest + * will hold the result + * @return dest + */ + public static Vector2d dFdyLinear( + double v0X, double v0Y, double f0X, double f0Y, + double v1X, double v1Y, double f1X, double f1Y, + double v2X, double v2Y, double f2X, double f2Y, + Vector2d dest) { + double v21X = v2X - v1X; + double v02X = v0X - v2X; + double den = (v1Y - v2Y) * v02X + v21X * (v0Y - v2Y); + double l3_1 = den - v21X - v02X; + double invDen = 1.0f / den; + dest.x = invDen * (v21X * f0X + v02X * f1X + l3_1 * f2X) - f2X; + dest.y = invDen * (v21X * f0Y + v02X * f1Y + l3_1 * f2Y) - f2Y; + return dest; + } + + /** + * Bilinearly interpolate the three-dimensional vector f over the given triangle and store the result in dest. + *

+ * Reference: https://en.wikipedia.org/ + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0X + * the x component of the value of f at the first vertex + * @param f0Y + * the y component of the value of f at the first vertex + * @param f0Z + * the z component of the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1X + * the x component of the value of f at the second vertex + * @param f1Y + * the y component of the value of f at the second vertex + * @param f1Z + * the z component of the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2X + * the x component of the value of f at the third vertex + * @param f2Y + * the y component of the value of f at the third vertex + * @param f2Z + * the z component of the value of f at the third vertex + * @param x + * the x coordinate of the point to interpolate f at + * @param y + * the y coordinate of the point to interpolate f at + * @param dest + * will hold the interpolation result + * @return dest + */ + public static Vector3d interpolateTriangle( + double v0X, double v0Y, double f0X, double f0Y, double f0Z, + double v1X, double v1Y, double f1X, double f1Y, double f1Z, + double v2X, double v2Y, double f2X, double f2Y, double f2Z, + double x, double y, Vector3d dest) { + // compute interpolation factors + Vector3d t = dest; + interpolationFactorsTriangle(v0X, v0Y, v1X, v1Y, v2X, v2Y, x, y, t); + // interpolate using these factors + return dest.set(t.x * f0X + t.y * f1X + t.z * f2X, + t.x * f0Y + t.y * f1Y + t.z * f2Y, + t.x * f0Z + t.y * f1Z + t.z * f2Z); + } + + /** + * Compute the interpolation factors (t0, t1, t2) in order to interpolate an arbitrary value over a given + * triangle at the given point (x, y). + *

+ * This method takes in the 2D vertex positions of the three vertices of a triangle and stores in dest the + * factors (t0, t1, t2) in the equation v' = v0 * t0 + v1 * t1 + v2 * t2 where (v0, v1, v2) are + * arbitrary (scalar or vector) values associated with the respective vertices of the triangle. The computed value v' + * is the interpolated value at the given position (x, y). + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param x + * the x coordinate of the point to interpolate at + * @param y + * the y coordinate of the point to interpolate at + * @param dest + * will hold the interpolation factors (t0, t1, t2) + * @return dest + */ + public static Vector3d interpolationFactorsTriangle( + double v0X, double v0Y, double v1X, double v1Y, double v2X, double v2Y, + double x, double y, Vector3d dest) { + double v12Y = v1Y - v2Y; + double v21X = v2X - v1X; + double v02X = v0X - v2X; + double yv2Y = y - v2Y; + double xv2X = x - v2X; + double v02Y = v0Y - v2Y; + double invDen = 1.0 / (v12Y * v02X + v21X * v02Y); + dest.x = (v12Y * xv2X + v21X * yv2Y) * invDen; + dest.y = (v02X * yv2Y - v02Y * xv2X) * invDen; + dest.z = 1.0 - dest.x - dest.y; + return dest; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Interpolationf.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Interpolationf.java new file mode 100644 index 000000000..a3e3f4233 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Interpolationf.java @@ -0,0 +1,337 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Contains various interpolation functions. + * + * @author Kai Burjack + */ +public class Interpolationf { + + /** + * Bilinearly interpolate the single scalar value f over the given triangle. + *

+ * Reference: https://en.wikipedia.org/ + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0 + * the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1 + * the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2 + * the value of f at the third vertex + * @param x + * the x coordinate of the point to interpolate f at + * @param y + * the y coordinate of the point to interpolate f at + * @return the interpolated value of f + */ + public static float interpolateTriangle( + float v0X, float v0Y, float f0, + float v1X, float v1Y, float f1, + float v2X, float v2Y, float f2, + float x, float y) { + float v12Y = v1Y - v2Y; + float v21X = v2X - v1X; + float v02X = v0X - v2X; + float yv2Y = y - v2Y; + float xv2X = x - v2X; + float v02Y = v0Y - v2Y; + float invDen = 1.0f / (v12Y * v02X + v21X * v02Y); + float l1 = (v12Y * xv2X + v21X * yv2Y) * invDen; + float l2 = (v02X * yv2Y - v02Y * xv2X) * invDen; + return l1 * f0 + l2 * f1 + (1.0f - l1 - l2) * f2; + } + + /** + * Bilinearly interpolate the two-dimensional vector f over the given triangle and store the result in dest. + *

+ * Reference: https://en.wikipedia.org/ + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0X + * the x component of the value of f at the first vertex + * @param f0Y + * the y component of the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1X + * the x component of the value of f at the second vertex + * @param f1Y + * the y component of the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2X + * the x component of the value of f at the third vertex + * @param f2Y + * the y component of the value of f at the third vertex + * @param x + * the x coordinate of the point to interpolate f at + * @param y + * the y coordinate of the point to interpolate f at + * @param dest + * will hold the interpolation result + * @return dest + */ + public static Vector2f interpolateTriangle( + float v0X, float v0Y, float f0X, float f0Y, + float v1X, float v1Y, float f1X, float f1Y, + float v2X, float v2Y, float f2X, float f2Y, + float x, float y, Vector2f dest) { + float v12Y = v1Y - v2Y; + float v21X = v2X - v1X; + float v02X = v0X - v2X; + float yv2Y = y - v2Y; + float xv2X = x - v2X; + float v02Y = v0Y - v2Y; + float invDen = 1.0f / (v12Y * v02X + v21X * v02Y); + float l1 = (v12Y * xv2X + v21X * yv2Y) * invDen; + float l2 = (v02X * yv2Y - v02Y * xv2X) * invDen; + float l3 = 1.0f - l1 - l2; + dest.x = l1 * f0X + l2 * f1X + l3 * f2X; + dest.y = l1 * f0Y + l2 * f1Y + l3 * f2Y; + return dest; + } + + /** + * Compute the first-order derivative of a linear two-dimensional function f with respect to X + * and store the result in dest. + *

+ * This method computes the constant rate of change for f given the three values of f + * at the specified three inputs (v0X, v0Y), (v1X, v1Y) and (v2X, v2Y). + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0X + * the x component of the value of f at the first vertex + * @param f0Y + * the y component of the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1X + * the x component of the value of f at the second vertex + * @param f1Y + * the y component of the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2X + * the x component of the value of f at the third vertex + * @param f2Y + * the y component of the value of f at the third vertex + * @param dest + * will hold the result + * @return dest + */ + public static Vector2f dFdxLinear( + float v0X, float v0Y, float f0X, float f0Y, + float v1X, float v1Y, float f1X, float f1Y, + float v2X, float v2Y, float f2X, float f2Y, Vector2f dest) { + float v12Y = v1Y - v2Y; + float v02Y = v0Y - v2Y; + float den = v12Y * (v0X - v2X) + (v2X - v1X) * v02Y; + float l3_1 = den - v12Y + v02Y; + float invDen = 1.0f / den; + dest.x = invDen * (v12Y * f0X - v02Y * f1X + l3_1 * f2X) - f2X; + dest.y = invDen * (v12Y * f0Y - v02Y * f1Y + l3_1 * f2Y) - f2Y; + return dest; + } + + /** + * Compute the first-order derivative of a linear two-dimensional function f with respect to Y + * and store the result in dest. + *

+ * This method computes the constant rate of change for f given the three values of f + * at the specified three inputs (v0X, v0Y), (v1X, v1Y) and (v2X, v2Y). + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0X + * the x component of the value of f at the first vertex + * @param f0Y + * the y component of the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1X + * the x component of the value of f at the second vertex + * @param f1Y + * the y component of the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2X + * the x component of the value of f at the third vertex + * @param f2Y + * the y component of the value of f at the third vertex + * @param dest + * will hold the result + * @return dest + */ + public static Vector2f dFdyLinear( + float v0X, float v0Y, float f0X, float f0Y, + float v1X, float v1Y, float f1X, float f1Y, + float v2X, float v2Y, float f2X, float f2Y, + Vector2f dest) { + float v21X = v2X - v1X; + float v02X = v0X - v2X; + float den = (v1Y - v2Y) * v02X + v21X * (v0Y - v2Y); + float l3_1 = den - v21X - v02X; + float invDen = 1.0f / den; + dest.x = invDen * (v21X * f0X + v02X * f1X + l3_1 * f2X) - f2X; + dest.y = invDen * (v21X * f0Y + v02X * f1Y + l3_1 * f2Y) - f2Y; + return dest; + } + + /** + * Bilinearly interpolate the three-dimensional vector f over the given triangle and store the result in dest. + *

+ * Reference: https://en.wikipedia.org/ + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param f0X + * the x component of the value of f at the first vertex + * @param f0Y + * the y component of the value of f at the first vertex + * @param f0Z + * the z component of the value of f at the first vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param f1X + * the x component of the value of f at the second vertex + * @param f1Y + * the y component of the value of f at the second vertex + * @param f1Z + * the z component of the value of f at the second vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param f2X + * the x component of the value of f at the third vertex + * @param f2Y + * the y component of the value of f at the third vertex + * @param f2Z + * the z component of the value of f at the third vertex + * @param x + * the x coordinate of the point to interpolate f at + * @param y + * the y coordinate of the point to interpolate f at + * @param dest + * will hold the interpolation result + * @return dest + */ + public static Vector3f interpolateTriangle( + float v0X, float v0Y, float f0X, float f0Y, float f0Z, + float v1X, float v1Y, float f1X, float f1Y, float f1Z, + float v2X, float v2Y, float f2X, float f2Y, float f2Z, + float x, float y, Vector3f dest) { + // compute interpolation factors + Vector3f t = dest; + interpolationFactorsTriangle(v0X, v0Y, v1X, v1Y, v2X, v2Y, x, y, t); + // interpolate using these factors + return dest.set(t.x * f0X + t.y * f1X + t.z * f2X, + t.x * f0Y + t.y * f1Y + t.z * f2Y, + t.x * f0Z + t.y * f1Z + t.z * f2Z); + } + + /** + * Compute the interpolation factors (t0, t1, t2) in order to interpolate an arbitrary value over a given + * triangle at the given point (x, y). + *

+ * This method takes in the 2D vertex positions of the three vertices of a triangle and stores in dest the + * factors (t0, t1, t2) in the equation v' = v0 * t0 + v1 * t1 + v2 * t2 where (v0, v1, v2) are + * arbitrary (scalar or vector) values associated with the respective vertices of the triangle. The computed value v' + * is the interpolated value at the given position (x, y). + * + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param x + * the x coordinate of the point to interpolate at + * @param y + * the y coordinate of the point to interpolate at + * @param dest + * will hold the interpolation factors (t0, t1, t2) + * @return dest + */ + public static Vector3f interpolationFactorsTriangle( + float v0X, float v0Y, float v1X, float v1Y, float v2X, float v2Y, + float x, float y, Vector3f dest) { + float v12Y = v1Y - v2Y; + float v21X = v2X - v1X; + float v02X = v0X - v2X; + float yv2Y = y - v2Y; + float xv2X = x - v2X; + float v02Y = v0Y - v2Y; + float invDen = 1.0f / (v12Y * v02X + v21X * v02Y); + dest.x = (v12Y * xv2X + v21X * yv2Y) * invDen; + dest.y = (v02X * yv2Y - v02Y * xv2X) * invDen; + dest.z = 1.0f - dest.x - dest.y; + return dest; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Intersectiond.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Intersectiond.java new file mode 100644 index 000000000..16edb1abb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Intersectiond.java @@ -0,0 +1,4789 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Contains intersection and distance tests for some 2D and 3D geometric primitives. + * + * @author Kai Burjack + */ +public class Intersectiond { + + /** + * Return value of + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, double, double, double, double, Vector3d)}, + * {@link #findClosestPointOnTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3d)}, + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #findClosestPointOnTriangle(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point is the first vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_VERTEX_0 = 1; + /** + * Return value of + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, double, double, double, double, Vector3d)}, + * {@link #findClosestPointOnTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3d)}, + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #findClosestPointOnTriangle(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point is the second vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_VERTEX_1 = 2; + /** + * Return value of + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, double, double, double, double, Vector3d)}, + * {@link #findClosestPointOnTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3d)}, + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #findClosestPointOnTriangle(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point is the third vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_VERTEX_2 = 3; + + /** + * Return value of + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, double, double, double, double, Vector3d)}, + * {@link #findClosestPointOnTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3d)}, + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #findClosestPointOnTriangle(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point lies on the edge between the first and second vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_EDGE_01 = 4; + /** + * Return value of + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, double, double, double, double, Vector3d)}, + * {@link #findClosestPointOnTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3d)}, + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #findClosestPointOnTriangle(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point lies on the edge between the second and third vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_EDGE_12 = 5; + /** + * Return value of + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, double, double, double, double, Vector3d)}, + * {@link #findClosestPointOnTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3d)}, + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #findClosestPointOnTriangle(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point lies on the edge between the third and first vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_EDGE_20 = 6; + + /** + * Return value of + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, double, double, double, double, Vector3d)}, + * {@link #findClosestPointOnTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3d)}, + * {@link #findClosestPointOnTriangle(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #findClosestPointOnTriangle(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point lies on the face of the triangle. + */ + public static final int POINT_ON_TRIANGLE_FACE = 7; + + /** + * Return value of {@link #intersectRayAar(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectRayAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} + * to indicate that the ray intersects the side of the axis-aligned rectangle with the minimum x coordinate. + */ + public static final int AAR_SIDE_MINX = 0; + /** + * Return value of {@link #intersectRayAar(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectRayAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} + * to indicate that the ray intersects the side of the axis-aligned rectangle with the minimum y coordinate. + */ + public static final int AAR_SIDE_MINY = 1; + /** + * Return value of {@link #intersectRayAar(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectRayAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} + * to indicate that the ray intersects the side of the axis-aligned rectangle with the maximum x coordinate. + */ + public static final int AAR_SIDE_MAXX = 2; + /** + * Return value of {@link #intersectRayAar(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectRayAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} + * to indicate that the ray intersects the side of the axis-aligned rectangle with the maximum y coordinate. + */ + public static final int AAR_SIDE_MAXY = 3; + + /** + * Return value of {@link #intersectLineSegmentAab(double, double, double, double, double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectLineSegmentAab(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector2d)} to indicate that the line segment does not intersect the axis-aligned box; + * or return value of {@link #intersectLineSegmentAar(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectLineSegmentAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} to indicate that the line segment does not intersect the axis-aligned rectangle. + */ + public static final int OUTSIDE = -1; + /** + * Return value of {@link #intersectLineSegmentAab(double, double, double, double, double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectLineSegmentAab(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector2d)} to indicate that one end point of the line segment lies inside of the axis-aligned box; + * or return value of {@link #intersectLineSegmentAar(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectLineSegmentAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} to indicate that one end point of the line segment lies inside of the axis-aligned rectangle. + */ + public static final int ONE_INTERSECTION = 1; + /** + * Return value of {@link #intersectLineSegmentAab(double, double, double, double, double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectLineSegmentAab(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector2d)} to indicate that the line segment intersects two sides of the axis-aligned box + * or lies on an edge or a side of the box; + * or return value of {@link #intersectLineSegmentAar(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectLineSegmentAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} to indicate that the line segment intersects two edges of the axis-aligned rectangle + * or lies on an edge of the rectangle. + */ + public static final int TWO_INTERSECTION = 2; + /** + * Return value of {@link #intersectLineSegmentAab(double, double, double, double, double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectLineSegmentAab(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector2d)} to indicate that the line segment lies completely inside of the axis-aligned box; + * or return value of {@link #intersectLineSegmentAar(double, double, double, double, double, double, double, double, Vector2d)} and + * {@link #intersectLineSegmentAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d)} to indicate that the line segment lies completely inside of the axis-aligned rectangle. + */ + public static final int INSIDE = 3; + + /** + * Test whether the plane with the general plane equation a*x + b*y + c*z + d = 0 intersects the sphere with center + * (centerX, centerY, centerZ) and radius. + *

+ * Reference: http://math.stackexchange.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radius + * the radius of the sphere + * @return true iff the plane intersects the sphere; false otherwise + */ + public static boolean testPlaneSphere( + double a, double b, double c, double d, + double centerX, double centerY, double centerZ, double radius) { + double denom = Math.sqrt(a * a + b * b + c * c); + double dist = (a * centerX + b * centerY + c * centerZ + d) / denom; + return -radius <= dist && dist <= radius; + } + + /** + * Test whether the plane with the general plane equation a*x + b*y + c*z + d = 0 intersects the sphere with center + * (centerX, centerY, centerZ) and radius, and store the center of the circle of + * intersection in the (x, y, z) components of the supplied vector and the radius of that circle in the w component. + *

+ * Reference: http://math.stackexchange.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radius + * the radius of the sphere + * @param intersectionCenterAndRadius + * will hold the center of the circle of intersection in the (x, y, z) components and the radius in the w component + * @return true iff the plane intersects the sphere; false otherwise + */ + public static boolean intersectPlaneSphere( + double a, double b, double c, double d, + double centerX, double centerY, double centerZ, double radius, + Vector4d intersectionCenterAndRadius) { + double invDenom = Math.invsqrt(a * a + b * b + c * c); + double dist = (a * centerX + b * centerY + c * centerZ + d) * invDenom; + if (-radius <= dist && dist <= radius) { + intersectionCenterAndRadius.x = centerX + dist * a * invDenom; + intersectionCenterAndRadius.y = centerY + dist * b * invDenom; + intersectionCenterAndRadius.z = centerZ + dist * c * invDenom; + intersectionCenterAndRadius.w = Math.sqrt(radius * radius - dist * dist); + return true; + } + return false; + } + + /** + * Test whether the plane with the general plane equation a*x + b*y + c*z + d = 0 intersects the moving sphere with center + * (cX, cY, cZ), radius and velocity (vX, vY, vZ), and store the point of intersection + * in the (x, y, z) components of the supplied vector and the time of intersection in the w component. + *

+ * The normal vector (a, b, c) of the plane equation needs to be normalized. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.5.3 "Intersecting Moving Sphere Against Plane" + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param cX + * the x coordinate of the center position of the sphere at t=0 + * @param cY + * the y coordinate of the center position of the sphere at t=0 + * @param cZ + * the z coordinate of the center position of the sphere at t=0 + * @param radius + * the sphere's radius + * @param vX + * the x component of the velocity of the sphere + * @param vY + * the y component of the velocity of the sphere + * @param vZ + * the z component of the velocity of the sphere + * @param pointAndTime + * will hold the point and time of intersection (if any) + * @return true iff the sphere intersects the plane; false otherwise + */ + public static boolean intersectPlaneSweptSphere( + double a, double b, double c, double d, + double cX, double cY, double cZ, double radius, + double vX, double vY, double vZ, + Vector4d pointAndTime) { + // Compute distance of sphere center to plane + double dist = a * cX + b * cY + c * cZ - d; + if (Math.abs(dist) <= radius) { + // The sphere is already overlapping the plane. Set time of + // intersection to zero and q to sphere center + pointAndTime.set(cX, cY, cZ, 0.0); + return true; + } + double denom = a * vX + b * vY + c * vZ; + if (denom * dist >= 0.0) { + // No intersection as sphere moving parallel to or away from plane + return false; + } + // Sphere is moving towards the plane + // Use +r in computations if sphere in front of plane, else -r + double r = dist > 0.0 ? radius : -radius; + double t = (r - dist) / denom; + pointAndTime.set( + cX + t * vX - r * a, + cY + t * vY - r * b, + cZ + t * vZ - r * c, + t); + return true; + } + + /** + * Test whether the plane with the general plane equation a*x + b*y + c*z + d = 0 intersects the sphere moving from center + * position (t0X, t0Y, t0Z) to (t1X, t1Y, t1Z) and having the given radius. + *

+ * The normal vector (a, b, c) of the plane equation needs to be normalized. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.5.3 "Intersecting Moving Sphere Against Plane" + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param t0X + * the x coordinate of the start position of the sphere + * @param t0Y + * the y coordinate of the start position of the sphere + * @param t0Z + * the z coordinate of the start position of the sphere + * @param r + * the sphere's radius + * @param t1X + * the x coordinate of the end position of the sphere + * @param t1Y + * the y coordinate of the end position of the sphere + * @param t1Z + * the z coordinate of the end position of the sphere + * @return true if the sphere intersects the plane; false otherwise + */ + public static boolean testPlaneSweptSphere( + double a, double b, double c, double d, + double t0X, double t0Y, double t0Z, double r, + double t1X, double t1Y, double t1Z) { + // Get the distance for both a and b from plane p + double adist = t0X * a + t0Y * b + t0Z * c - d; + double bdist = t1X * a + t1Y * b + t1Z * c - d; + // Intersects if on different sides of plane (distances have different signs) + if (adist * bdist < 0.0) return true; + // Intersects if start or end position within radius from plane + if (Math.abs(adist) <= r || Math.abs(bdist) <= r) return true; + // No intersection + return false; + } + + /** + * Test whether the axis-aligned box with minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ) + * intersects the plane with the general equation a*x + b*y + c*z + d = 0. + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the z coordinate of the maximum corner of the axis-aligned box + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return true iff the axis-aligned box intersects the plane; false otherwise + */ + public static boolean testAabPlane( + double minX, double minY, double minZ, + double maxX, double maxY, double maxZ, + double a, double b, double c, double d) { + double pX, pY, pZ, nX, nY, nZ; + if (a > 0.0) { + pX = maxX; + nX = minX; + } else { + pX = minX; + nX = maxX; + } + if (b > 0.0) { + pY = maxY; + nY = minY; + } else { + pY = minY; + nY = maxY; + } + if (c > 0.0) { + pZ = maxZ; + nZ = minZ; + } else { + pZ = minZ; + nZ = maxZ; + } + double distN = d + a * nX + b * nY + c * nZ; + double distP = d + a * pX + b * pY + c * pZ; + return distN <= 0.0 && distP >= 0.0; + } + + /** + * Test whether the axis-aligned box with minimum corner min and maximum corner max + * intersects the plane with the general equation a*x + b*y + c*z + d = 0. + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return true iff the axis-aligned box intersects the plane; false otherwise + */ + public static boolean testAabPlane(Vector3dc min, Vector3dc max, double a, double b, double c, double d) { + return testAabPlane(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), a, b, c, d); + } + + /** + * Test whether the axis-aligned box with minimum corner (minXA, minYA, minZA) and maximum corner (maxXA, maxYA, maxZA) + * intersects the axis-aligned box with minimum corner (minXB, minYB, minZB) and maximum corner (maxXB, maxYB, maxZB). + * + * @param minXA + * the x coordinate of the minimum corner of the first axis-aligned box + * @param minYA + * the y coordinate of the minimum corner of the first axis-aligned box + * @param minZA + * the z coordinate of the minimum corner of the first axis-aligned box + * @param maxXA + * the x coordinate of the maximum corner of the first axis-aligned box + * @param maxYA + * the y coordinate of the maximum corner of the first axis-aligned box + * @param maxZA + * the z coordinate of the maximum corner of the first axis-aligned box + * @param minXB + * the x coordinate of the minimum corner of the second axis-aligned box + * @param minYB + * the y coordinate of the minimum corner of the second axis-aligned box + * @param minZB + * the z coordinate of the minimum corner of the second axis-aligned box + * @param maxXB + * the x coordinate of the maximum corner of the second axis-aligned box + * @param maxYB + * the y coordinate of the maximum corner of the second axis-aligned box + * @param maxZB + * the z coordinate of the maximum corner of the second axis-aligned box + * @return true iff both axis-aligned boxes intersect; false otherwise + */ + public static boolean testAabAab( + double minXA, double minYA, double minZA, + double maxXA, double maxYA, double maxZA, + double minXB, double minYB, double minZB, + double maxXB, double maxYB, double maxZB) { + return maxXA >= minXB && maxYA >= minYB && maxZA >= minZB && + minXA <= maxXB && minYA <= maxYB && minZA <= maxZB; + } + + /** + * Test whether the axis-aligned box with minimum corner minA and maximum corner maxA + * intersects the axis-aligned box with minimum corner minB and maximum corner maxB. + * + * @param minA + * the minimum corner of the first axis-aligned box + * @param maxA + * the maximum corner of the first axis-aligned box + * @param minB + * the minimum corner of the second axis-aligned box + * @param maxB + * the maximum corner of the second axis-aligned box + * @return true iff both axis-aligned boxes intersect; false otherwise + */ + public static boolean testAabAab(Vector3dc minA, Vector3dc maxA, Vector3dc minB, Vector3dc maxB) { + return testAabAab(minA.x(), minA.y(), minA.z(), maxA.x(), maxA.y(), maxA.z(), minB.x(), minB.y(), minB.z(), maxB.x(), maxB.y(), maxB.z()); + } + + /** + * Test whether two oriented boxes given via their center position, orientation and half-size, intersect. + *

+ * The orientation of a box is given as three unit vectors spanning the local orthonormal basis of the box. + *

+ * The size is given as the half-size along each of the unit vectors defining the orthonormal basis. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 4.4.1 "OBB-OBB Intersection" + * + * @param b0c + * the center of the first box + * @param b0uX + * the local X unit vector of the first box + * @param b0uY + * the local Y unit vector of the first box + * @param b0uZ + * the local Z unit vector of the first box + * @param b0hs + * the half-size of the first box + * @param b1c + * the center of the second box + * @param b1uX + * the local X unit vector of the second box + * @param b1uY + * the local Y unit vector of the second box + * @param b1uZ + * the local Z unit vector of the second box + * @param b1hs + * the half-size of the second box + * @return true if both boxes intersect; false otherwise + */ + public static boolean testObOb( + Vector3d b0c, Vector3d b0uX, Vector3d b0uY, Vector3d b0uZ, Vector3d b0hs, + Vector3d b1c, Vector3d b1uX, Vector3d b1uY, Vector3d b1uZ, Vector3d b1hs) { + return testObOb( + b0c.x, b0c.y, b0c.z, b0uX.x, b0uX.y, b0uX.z, b0uY.x, b0uY.y, b0uY.z, b0uZ.x, b0uZ.y, b0uZ.z, b0hs.x, b0hs.y, b0hs.z, + b1c.x, b1c.y, b1c.z, b1uX.x, b1uX.y, b1uX.z, b1uY.x, b1uY.y, b1uY.z, b1uZ.x, b1uZ.y, b1uZ.z, b1hs.x, b1hs.y, b1hs.z); + } + + /** + * Test whether two oriented boxes given via their center position, orientation and half-size, intersect. + *

+ * The orientation of a box is given as three unit vectors spanning the local orthonormal basis of the box. + *

+ * The size is given as the half-size along each of the unit vectors defining the orthonormal basis. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 4.4.1 "OBB-OBB Intersection" + * + * @param b0cX + * the x coordinate of the center of the first box + * @param b0cY + * the y coordinate of the center of the first box + * @param b0cZ + * the z coordinate of the center of the first box + * @param b0uXx + * the x coordinate of the local X unit vector of the first box + * @param b0uXy + * the y coordinate of the local X unit vector of the first box + * @param b0uXz + * the z coordinate of the local X unit vector of the first box + * @param b0uYx + * the x coordinate of the local Y unit vector of the first box + * @param b0uYy + * the y coordinate of the local Y unit vector of the first box + * @param b0uYz + * the z coordinate of the local Y unit vector of the first box + * @param b0uZx + * the x coordinate of the local Z unit vector of the first box + * @param b0uZy + * the y coordinate of the local Z unit vector of the first box + * @param b0uZz + * the z coordinate of the local Z unit vector of the first box + * @param b0hsX + * the half-size of the first box along its local X axis + * @param b0hsY + * the half-size of the first box along its local Y axis + * @param b0hsZ + * the half-size of the first box along its local Z axis + * @param b1cX + * the x coordinate of the center of the second box + * @param b1cY + * the y coordinate of the center of the second box + * @param b1cZ + * the z coordinate of the center of the second box + * @param b1uXx + * the x coordinate of the local X unit vector of the second box + * @param b1uXy + * the y coordinate of the local X unit vector of the second box + * @param b1uXz + * the z coordinate of the local X unit vector of the second box + * @param b1uYx + * the x coordinate of the local Y unit vector of the second box + * @param b1uYy + * the y coordinate of the local Y unit vector of the second box + * @param b1uYz + * the z coordinate of the local Y unit vector of the second box + * @param b1uZx + * the x coordinate of the local Z unit vector of the second box + * @param b1uZy + * the y coordinate of the local Z unit vector of the second box + * @param b1uZz + * the z coordinate of the local Z unit vector of the second box + * @param b1hsX + * the half-size of the second box along its local X axis + * @param b1hsY + * the half-size of the second box along its local Y axis + * @param b1hsZ + * the half-size of the second box along its local Z axis + * @return true if both boxes intersect; false otherwise + */ + public static boolean testObOb( + double b0cX, double b0cY, double b0cZ, double b0uXx, double b0uXy, double b0uXz, double b0uYx, double b0uYy, double b0uYz, double b0uZx, double b0uZy, double b0uZz, double b0hsX, double b0hsY, double b0hsZ, + double b1cX, double b1cY, double b1cZ, double b1uXx, double b1uXy, double b1uXz, double b1uYx, double b1uYy, double b1uYz, double b1uZx, double b1uZy, double b1uZz, double b1hsX, double b1hsY, double b1hsZ) { + double ra, rb; + // Compute rotation matrix expressing b in a's coordinate frame + double rm00 = b0uXx * b1uXx + b0uYx * b1uYx + b0uZx * b1uZx; + double rm10 = b0uXx * b1uXy + b0uYx * b1uYy + b0uZx * b1uZy; + double rm20 = b0uXx * b1uXz + b0uYx * b1uYz + b0uZx * b1uZz; + double rm01 = b0uXy * b1uXx + b0uYy * b1uYx + b0uZy * b1uZx; + double rm11 = b0uXy * b1uXy + b0uYy * b1uYy + b0uZy * b1uZy; + double rm21 = b0uXy * b1uXz + b0uYy * b1uYz + b0uZy * b1uZz; + double rm02 = b0uXz * b1uXx + b0uYz * b1uYx + b0uZz * b1uZx; + double rm12 = b0uXz * b1uXy + b0uYz * b1uYy + b0uZz * b1uZy; + double rm22 = b0uXz * b1uXz + b0uYz * b1uYz + b0uZz * b1uZz; + // Compute common subexpressions. Add in an epsilon term to + // counteract arithmetic errors when two edges are parallel and + // their cross product is (near) null (see text for details) + double EPSILON = 1E-8; + double arm00 = Math.abs(rm00) + EPSILON; + double arm01 = Math.abs(rm01) + EPSILON; + double arm02 = Math.abs(rm02) + EPSILON; + double arm10 = Math.abs(rm10) + EPSILON; + double arm11 = Math.abs(rm11) + EPSILON; + double arm12 = Math.abs(rm12) + EPSILON; + double arm20 = Math.abs(rm20) + EPSILON; + double arm21 = Math.abs(rm21) + EPSILON; + double arm22 = Math.abs(rm22) + EPSILON; + // Compute translation vector t + double tx = b1cX - b0cX, ty = b1cY - b0cY, tz = b1cZ - b0cZ; + // Bring translation into a's coordinate frame + double tax = tx * b0uXx + ty * b0uXy + tz * b0uXz; + double tay = tx * b0uYx + ty * b0uYy + tz * b0uYz; + double taz = tx * b0uZx + ty * b0uZy + tz * b0uZz; + // Test axes L = A0, L = A1, L = A2 + ra = b0hsX; + rb = b1hsX * arm00 + b1hsY * arm01 + b1hsZ * arm02; + if (Math.abs(tax) > ra + rb) return false; + ra = b0hsY; + rb = b1hsX * arm10 + b1hsY * arm11 + b1hsZ * arm12; + if (Math.abs(tay) > ra + rb) return false; + ra = b0hsZ; + rb = b1hsX * arm20 + b1hsY * arm21 + b1hsZ * arm22; + if (Math.abs(taz) > ra + rb) return false; + // Test axes L = B0, L = B1, L = B2 + ra = b0hsX * arm00 + b0hsY * arm10 + b0hsZ * arm20; + rb = b1hsX; + if (Math.abs(tax * rm00 + tay * rm10 + taz * rm20) > ra + rb) return false; + ra = b0hsX * arm01 + b0hsY * arm11 + b0hsZ * arm21; + rb = b1hsY; + if (Math.abs(tax * rm01 + tay * rm11 + taz * rm21) > ra + rb) return false; + ra = b0hsX * arm02 + b0hsY * arm12 + b0hsZ * arm22; + rb = b1hsZ; + if (Math.abs(tax * rm02 + tay * rm12 + taz * rm22) > ra + rb) return false; + // Test axis L = A0 x B0 + ra = b0hsY * arm20 + b0hsZ * arm10; + rb = b1hsY * arm02 + b1hsZ * arm01; + if (Math.abs(taz * rm10 - tay * rm20) > ra + rb) return false; + // Test axis L = A0 x B1 + ra = b0hsY * arm21 + b0hsZ * arm11; + rb = b1hsX * arm02 + b1hsZ * arm00; + if (Math.abs(taz * rm11 - tay * rm21) > ra + rb) return false; + // Test axis L = A0 x B2 + ra = b0hsY * arm22 + b0hsZ * arm12; + rb = b1hsX * arm01 + b1hsY * arm00; + if (Math.abs(taz * rm12 - tay * rm22) > ra + rb) return false; + // Test axis L = A1 x B0 + ra = b0hsX * arm20 + b0hsZ * arm00; + rb = b1hsY * arm12 + b1hsZ * arm11; + if (Math.abs(tax * rm20 - taz * rm00) > ra + rb) return false; + // Test axis L = A1 x B1 + ra = b0hsX * arm21 + b0hsZ * arm01; + rb = b1hsX * arm12 + b1hsZ * arm10; + if (Math.abs(tax * rm21 - taz * rm01) > ra + rb) return false; + // Test axis L = A1 x B2 + ra = b0hsX * arm22 + b0hsZ * arm02; + rb = b1hsX * arm11 + b1hsY * arm10; + if (Math.abs(tax * rm22 - taz * rm02) > ra + rb) return false; + // Test axis L = A2 x B0 + ra = b0hsX * arm10 + b0hsY * arm00; + rb = b1hsY * arm22 + b1hsZ * arm21; + if (Math.abs(tay * rm00 - tax * rm10) > ra + rb) return false; + // Test axis L = A2 x B1 + ra = b0hsX * arm11 + b0hsY * arm01; + rb = b1hsX * arm22 + b1hsZ * arm20; + if (Math.abs(tay * rm01 - tax * rm11) > ra + rb) return false; + // Test axis L = A2 x B2 + ra = b0hsX * arm12 + b0hsY * arm02; + rb = b1hsX * arm21 + b1hsY * arm20; + if (Math.abs(tay * rm02 - tax * rm12) > ra + rb) return false; + // Since no separating axis is found, the OBBs must be intersecting + return true; + } + + /** + * Test whether the one sphere with center (aX, aY, aZ) and square radius radiusSquaredA intersects the other + * sphere with center (bX, bY, bZ) and square radius radiusSquaredB, and store the center of the circle of + * intersection in the (x, y, z) components of the supplied vector and the radius of that circle in the w component. + *

+ * The normal vector of the circle of intersection can simply be obtained by subtracting the center of either sphere from the other. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param aX + * the x coordinate of the first sphere's center + * @param aY + * the y coordinate of the first sphere's center + * @param aZ + * the z coordinate of the first sphere's center + * @param radiusSquaredA + * the square of the first sphere's radius + * @param bX + * the x coordinate of the second sphere's center + * @param bY + * the y coordinate of the second sphere's center + * @param bZ + * the z coordinate of the second sphere's center + * @param radiusSquaredB + * the square of the second sphere's radius + * @param centerAndRadiusOfIntersectionCircle + * will hold the center of the circle of intersection in the (x, y, z) components and the radius in the w component + * @return true iff both spheres intersect; false otherwise + */ + public static boolean intersectSphereSphere( + double aX, double aY, double aZ, double radiusSquaredA, + double bX, double bY, double bZ, double radiusSquaredB, + Vector4d centerAndRadiusOfIntersectionCircle) { + double dX = bX - aX, dY = bY - aY, dZ = bZ - aZ; + double distSquared = dX * dX + dY * dY + dZ * dZ; + double h = 0.5 + (radiusSquaredA - radiusSquaredB) / distSquared; + double r_i = radiusSquaredA - h * h * distSquared; + if (r_i >= 0.0) { + centerAndRadiusOfIntersectionCircle.x = aX + h * dX; + centerAndRadiusOfIntersectionCircle.y = aY + h * dY; + centerAndRadiusOfIntersectionCircle.z = aZ + h * dZ; + centerAndRadiusOfIntersectionCircle.w = Math.sqrt(r_i); + return true; + } + return false; + } + + /** + * Test whether the one sphere with center centerA and square radius radiusSquaredA intersects the other + * sphere with center centerB and square radius radiusSquaredB, and store the center of the circle of + * intersection in the (x, y, z) components of the supplied vector and the radius of that circle in the w component. + *

+ * The normal vector of the circle of intersection can simply be obtained by subtracting the center of either sphere from the other. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param centerA + * the first sphere's center + * @param radiusSquaredA + * the square of the first sphere's radius + * @param centerB + * the second sphere's center + * @param radiusSquaredB + * the square of the second sphere's radius + * @param centerAndRadiusOfIntersectionCircle + * will hold the center of the circle of intersection in the (x, y, z) components and the radius in the w component + * @return true iff both spheres intersect; false otherwise + */ + public static boolean intersectSphereSphere(Vector3dc centerA, double radiusSquaredA, Vector3dc centerB, double radiusSquaredB, Vector4d centerAndRadiusOfIntersectionCircle) { + return intersectSphereSphere(centerA.x(), centerA.y(), centerA.z(), radiusSquaredA, centerB.x(), centerB.y(), centerB.z(), radiusSquaredB, centerAndRadiusOfIntersectionCircle); + } + + /** + * Test whether the given sphere with center (sX, sY, sZ) intersects the triangle given by its three vertices, and if they intersect + * store the point of intersection into result. + *

+ * This method also returns whether the point of intersection is on one of the triangle's vertices, edges or on the face. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.2.7 "Testing Sphere Against Triangle" + * + * @param sX + * the x coordinate of the sphere's center + * @param sY + * the y coordinate of the sphere's center + * @param sZ + * the z coordinate of the sphere's center + * @param sR + * the sphere's radius + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v0Z + * the z coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v1Z + * the z coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @param v2Z + * the z coordinate of the third vertex of the triangle + * @param result + * will hold the point of intersection + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} or 0 + */ + public static int intersectSphereTriangle( + double sX, double sY, double sZ, double sR, + double v0X, double v0Y, double v0Z, + double v1X, double v1Y, double v1Z, + double v2X, double v2Y, double v2Z, + Vector3d result) { + int closest = findClosestPointOnTriangle(v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z, sX, sY, sZ, result); + double vX = result.x - sX, vY = result.y - sY, vZ = result.z - sZ; + double dot = vX * vX + vY * vY + vZ * vZ; + if (dot <= sR * sR) { + return closest; + } + return 0; + } + + /** + * Test whether the one sphere with center (aX, aY, aZ) and square radius radiusSquaredA intersects the other + * sphere with center (bX, bY, bZ) and square radius radiusSquaredB. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param aX + * the x coordinate of the first sphere's center + * @param aY + * the y coordinate of the first sphere's center + * @param aZ + * the z coordinate of the first sphere's center + * @param radiusSquaredA + * the square of the first sphere's radius + * @param bX + * the x coordinate of the second sphere's center + * @param bY + * the y coordinate of the second sphere's center + * @param bZ + * the z coordinate of the second sphere's center + * @param radiusSquaredB + * the square of the second sphere's radius + * @return true iff both spheres intersect; false otherwise + */ + public static boolean testSphereSphere( + double aX, double aY, double aZ, double radiusSquaredA, + double bX, double bY, double bZ, double radiusSquaredB) { + double dX = bX - aX, dY = bY - aY, dZ = bZ - aZ; + double distSquared = dX * dX + dY * dY + dZ * dZ; + double h = 0.5 + (radiusSquaredA - radiusSquaredB) / distSquared; + double r_i = radiusSquaredA - h * h * distSquared; + return r_i >= 0.0; + } + + /** + * Test whether the one sphere with center centerA and square radius radiusSquaredA intersects the other + * sphere with center centerB and square radius radiusSquaredB. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param centerA + * the first sphere's center + * @param radiusSquaredA + * the square of the first sphere's radius + * @param centerB + * the second sphere's center + * @param radiusSquaredB + * the square of the second sphere's radius + * @return true iff both spheres intersect; false otherwise + */ + public static boolean testSphereSphere(Vector3dc centerA, double radiusSquaredA, Vector3dc centerB, double radiusSquaredB) { + return testSphereSphere(centerA.x(), centerA.y(), centerA.z(), radiusSquaredA, centerB.x(), centerB.y(), centerB.z(), radiusSquaredB); + } + + /** + * Determine the signed distance of the given point (pointX, pointY, pointZ) to the plane specified via its general plane equation + * a*x + b*y + c*z + d = 0. + * + * @param pointX + * the x coordinate of the point + * @param pointY + * the y coordinate of the point + * @param pointZ + * the z coordinate of the point + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return the distance between the point and the plane + */ + public static double distancePointPlane(double pointX, double pointY, double pointZ, double a, double b, double c, double d) { + double denom = Math.sqrt(a * a + b * b + c * c); + return (a * pointX + b * pointY + c * pointZ + d) / denom; + } + + /** + * Determine the signed distance of the given point (pointX, pointY, pointZ) to the plane of the triangle specified by its three points + * (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z). + *

+ * If the point lies on the front-facing side of the triangle's plane, that is, if the triangle has counter-clockwise winding order + * as seen from the point, then this method returns a positive number. + * + * @param pointX + * the x coordinate of the point + * @param pointY + * the y coordinate of the point + * @param pointZ + * the z coordinate of the point + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v0Z + * the z coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v1Z + * the z coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @param v2Z + * the z coordinate of the third vertex of the triangle + * @return the signed distance between the point and the plane of the triangle + */ + public static double distancePointPlane(double pointX, double pointY, double pointZ, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z) { + double v1Y0Y = v1Y - v0Y; + double v2Z0Z = v2Z - v0Z; + double v2Y0Y = v2Y - v0Y; + double v1Z0Z = v1Z - v0Z; + double v2X0X = v2X - v0X; + double v1X0X = v1X - v0X; + double a = v1Y0Y * v2Z0Z - v2Y0Y * v1Z0Z; + double b = v1Z0Z * v2X0X - v2Z0Z * v1X0X; + double c = v1X0X * v2Y0Y - v2X0X * v1Y0Y; + double d = -(a * v0X + b * v0Y + c * v0Z); + return distancePointPlane(pointX, pointY, pointZ, a, b, c, d); + } + + /** + * Test whether the ray with given origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) intersects the plane + * containing the given point (pointX, pointY, pointZ) and having the normal (normalX, normalY, normalZ), and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the plane, because it is either parallel to the plane or its direction points + * away from the plane or the ray's origin is on the negative side of the plane (i.e. the plane's normal points away from the ray's origin). + *

+ * Reference: https://www.siggraph.org/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param pointX + * the x coordinate of a point on the plane + * @param pointY + * the y coordinate of a point on the plane + * @param pointZ + * the z coordinate of a point on the plane + * @param normalX + * the x coordinate of the plane's normal + * @param normalY + * the y coordinate of the plane's normal + * @param normalZ + * the z coordinate of the plane's normal + * @param epsilon + * some small epsilon for when the ray is parallel to the plane + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the plane; -1.0 otherwise + */ + public static double intersectRayPlane(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double pointX, double pointY, double pointZ, double normalX, double normalY, double normalZ, double epsilon) { + double denom = normalX * dirX + normalY * dirY + normalZ * dirZ; + if (denom < epsilon) { + double t = ((pointX - originX) * normalX + (pointY - originY) * normalY + (pointZ - originZ) * normalZ) / denom; + if (t >= 0.0) + return t; + } + return -1.0; + } + + /** + * Test whether the ray with given origin and direction dir intersects the plane + * containing the given point and having the given normal, and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the plane, because it is either parallel to the plane or its direction points + * away from the plane or the ray's origin is on the negative side of the plane (i.e. the plane's normal points away from the ray's origin). + *

+ * Reference: https://www.siggraph.org/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param point + * a point on the plane + * @param normal + * the plane's normal + * @param epsilon + * some small epsilon for when the ray is parallel to the plane + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the plane; -1.0 otherwise + */ + public static double intersectRayPlane(Vector3dc origin, Vector3dc dir, Vector3dc point, Vector3dc normal, double epsilon) { + return intersectRayPlane(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), point.x(), point.y(), point.z(), normal.x(), normal.y(), normal.z(), epsilon); + } + + /** + * Test whether the ray with given origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) intersects the plane + * given as the general plane equation a*x + b*y + c*z + d = 0, and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the plane, because it is either parallel to the plane or its direction points + * away from the plane or the ray's origin is on the negative side of the plane (i.e. the plane's normal points away from the ray's origin). + *

+ * Reference: https://www.siggraph.org/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param epsilon + * some small epsilon for when the ray is parallel to the plane + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the plane; -1.0 otherwise + */ + public static double intersectRayPlane(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double a, double b, double c, double d, double epsilon) { + double denom = a * dirX + b * dirY + c * dirZ; + if (denom < 0.0) { + double t = -(a * originX + b * originY + c * originZ + d) / denom; + if (t >= 0.0) + return t; + } + return -1.0; + } + + /** + * Test whether the axis-aligned box with minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ) + * intersects the sphere with the given center (centerX, centerY, centerZ) and square radius radiusSquared. + *

+ * Reference: http://stackoverflow.com + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the z coordinate of the maximum corner of the axis-aligned box + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radiusSquared + * the square of the sphere's radius + * @return true iff the axis-aligned box intersects the sphere; false otherwise + */ + public static boolean testAabSphere( + double minX, double minY, double minZ, + double maxX, double maxY, double maxZ, + double centerX, double centerY, double centerZ, double radiusSquared) { + double radius2 = radiusSquared; + if (centerX < minX) { + double d = (centerX - minX); + radius2 -= d * d; + } else if (centerX > maxX) { + double d = (centerX - maxX); + radius2 -= d * d; + } + if (centerY < minY) { + double d = (centerY - minY); + radius2 -= d * d; + } else if (centerY > maxY) { + double d = (centerY - maxY); + radius2 -= d * d; + } + if (centerZ < minZ) { + double d = (centerZ - minZ); + radius2 -= d * d; + } else if (centerZ > maxZ) { + double d = (centerZ - maxZ); + radius2 -= d * d; + } + return radius2 >= 0.0; + } + + /** + * Test whether the axis-aligned box with minimum corner min and maximum corner max + * intersects the sphere with the given center and square radius radiusSquared. + *

+ * Reference: http://stackoverflow.com + * + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param center + * the sphere's center + * @param radiusSquared + * the squared of the sphere's radius + * @return true iff the axis-aligned box intersects the sphere; false otherwise + */ + public static boolean testAabSphere(Vector3dc min, Vector3dc max, Vector3dc center, double radiusSquared) { + return testAabSphere(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), center.x(), center.y(), center.z(), radiusSquared); + } + + /** + * Find the point on the given plane which is closest to the specified point (pX, pY, pZ) and store the result in result. + * + * @param aX + * the x coordinate of one point on the plane + * @param aY + * the y coordinate of one point on the plane + * @param aZ + * the z coordinate of one point on the plane + * @param nX + * the x coordinate of the unit normal of the plane + * @param nY + * the y coordinate of the unit normal of the plane + * @param nZ + * the z coordinate of the unit normal of the plane + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the z coordinate of the point + * @param result + * will hold the result + * @return result + */ + public static Vector3d findClosestPointOnPlane(double aX, double aY, double aZ, double nX, double nY, double nZ, double pX, double pY, double pZ, Vector3d result) { + double d = -(nX * aX + nY * aY + nZ * aZ); + double t = nX * pX + nY * pY + nZ * pZ - d; + result.x = pX - t * nX; + result.y = pY - t * nY; + result.z = pZ - t * nZ; + return result; + } + + /** + * Find the point on the given line segment which is closest to the specified point (pX, pY, pZ), and store the result in result. + * + * @param aX + * the x coordinate of the first end point of the line segment + * @param aY + * the y coordinate of the first end point of the line segment + * @param aZ + * the z coordinate of the first end point of the line segment + * @param bX + * the x coordinate of the second end point of the line segment + * @param bY + * the y coordinate of the second end point of the line segment + * @param bZ + * the z coordinate of the second end point of the line segment + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the z coordinate of the point + * @param result + * will hold the result + * @return result + */ + public static Vector3d findClosestPointOnLineSegment(double aX, double aY, double aZ, double bX, double bY, double bZ, double pX, double pY, double pZ, Vector3d result) { + double abX = bX - aX, abY = bY - aY, abZ = bZ - aZ; + double t = ((pX - aX) * abX + (pY - aY) * abY + (pZ - aZ) * abZ) / (abX * abX + abY * abY + abZ * abZ); + if (t < 0.0) t = 0.0; + if (t > 1.0) t = 1.0; + result.x = aX + t * abX; + result.y = aY + t * abY; + result.z = aZ + t * abZ; + return result; + } + + /** + * Find the closest points on the two line segments, store the point on the first line segment in resultA and + * the point on the second line segment in resultB, and return the square distance between both points. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.9 "Closest Points of Two Line Segments" + * + * @param a0X + * the x coordinate of the first line segment's first end point + * @param a0Y + * the y coordinate of the first line segment's first end point + * @param a0Z + * the z coordinate of the first line segment's first end point + * @param a1X + * the x coordinate of the first line segment's second end point + * @param a1Y + * the y coordinate of the first line segment's second end point + * @param a1Z + * the z coordinate of the first line segment's second end point + * @param b0X + * the x coordinate of the second line segment's first end point + * @param b0Y + * the y coordinate of the second line segment's first end point + * @param b0Z + * the z coordinate of the second line segment's first end point + * @param b1X + * the x coordinate of the second line segment's second end point + * @param b1Y + * the y coordinate of the second line segment's second end point + * @param b1Z + * the z coordinate of the second line segment's second end point + * @param resultA + * will hold the point on the first line segment + * @param resultB + * will hold the point on the second line segment + * @return the square distance between the two closest points + */ + public static double findClosestPointsLineSegments( + double a0X, double a0Y, double a0Z, double a1X, double a1Y, double a1Z, + double b0X, double b0Y, double b0Z, double b1X, double b1Y, double b1Z, + Vector3d resultA, Vector3d resultB) { + double d1x = a1X - a0X, d1y = a1Y - a0Y, d1z = a1Z - a0Z; + double d2x = b1X - b0X, d2y = b1Y - b0Y, d2z = b1Z - b0Z; + double rX = a0X - b0X, rY = a0Y - b0Y, rZ = a0Z - b0Z; + double a = d1x * d1x + d1y * d1y + d1z * d1z; + double e = d2x * d2x + d2y * d2y + d2z * d2z; + double f = d2x * rX + d2y * rY + d2z * rZ; + double EPSILON = 1E-8; + double s, t; + if (a <= EPSILON && e <= EPSILON) { + // Both segments degenerate into points + resultA.set(a0X, a0Y, a0Z); + resultB.set(b0X, b0Y, b0Z); + return resultA.dot(resultB); + } + if (a <= EPSILON) { + // First segment degenerates into a point + s = 0.0; + t = f / e; + t = Math.min(Math.max(t, 0.0), 1.0); + } else { + double c = d1x * rX + d1y * rY + d1z * rZ; + if (e <= EPSILON) { + // Second segment degenerates into a point + t = 0.0; + s = Math.min(Math.max(-c / a, 0.0), 1.0); + } else { + // The general nondegenerate case starts here + double b = d1x * d2x + d1y * d2y + d1z * d2z; + double denom = a * e - b * b; + // If segments not parallel, compute closest point on L1 to L2 and + // clamp to segment S1. Else pick arbitrary s (here 0) + if (denom != 0.0) + s = Math.min(Math.max((b*f - c*e) / denom, 0.0), 1.0); + else + s = 0.0; + // Compute point on L2 closest to S1(s) using + // t = Dot((P1 + D1*s) - P2,D2) / Dot(D2,D2) = (b*s + f) / e + t = (b * s + f) / e; + // If t in [0,1] done. Else clamp t, recompute s for the new value + // of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a + // and clamp s to [0, 1] + if (t < 0.0) { + t = 0.0; + s = Math.min(Math.max(-c / a, 0.0), 1.0); + } else if (t > 1.0) { + t = 1.0; + s = Math.min(Math.max((b - c) / a, 0.0), 1.0); + } + } + } + resultA.set(a0X + d1x * s, a0Y + d1y * s, a0Z + d1z * s); + resultB.set(b0X + d2x * t, b0Y + d2y * t, b0Z + d2z * t); + double dX = resultA.x - resultB.x, dY = resultA.y - resultB.y, dZ = resultA.z - resultB.z; + return dX*dX + dY*dY + dZ*dZ; + } + + /** + * Find the closest points on a line segment and a triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.10 "Closest Points of a Line Segment and a Triangle" + * + * @param aX + * the x coordinate of the line segment's first end point + * @param aY + * the y coordinate of the line segment's first end point + * @param aZ + * the z coordinate of the line segment's first end point + * @param bX + * the x coordinate of the line segment's second end point + * @param bY + * the y coordinate of the line segment's second end point + * @param bZ + * the z coordinate of the line segment's second end point + * @param v0X + * the x coordinate of the triangle's first vertex + * @param v0Y + * the y coordinate of the triangle's first vertex + * @param v0Z + * the z coordinate of the triangle's first vertex + * @param v1X + * the x coordinate of the triangle's second vertex + * @param v1Y + * the y coordinate of the triangle's second vertex + * @param v1Z + * the z coordinate of the triangle's second vertex + * @param v2X + * the x coordinate of the triangle's third vertex + * @param v2Y + * the y coordinate of the triangle's third vertex + * @param v2Z + * the z coordinate of the triangle's third vertex + * @param lineSegmentResult + * will hold the closest point on the line segment + * @param triangleResult + * will hold the closest point on the triangle + * @return the square distance of the closest points + */ + public static double findClosestPointsLineSegmentTriangle( + double aX, double aY, double aZ, double bX, double bY, double bZ, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z, + Vector3d lineSegmentResult, Vector3d triangleResult) { + double min, d; + double minlsX, minlsY, minlsZ, mintX, mintY, mintZ; + // AB -> V0V1 + d = findClosestPointsLineSegments(aX, aY, aZ, bX, bY, bZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, lineSegmentResult, triangleResult); + min = d; + minlsX = lineSegmentResult.x; minlsY = lineSegmentResult.y; minlsZ = lineSegmentResult.z; + mintX = triangleResult.x; mintY = triangleResult.y; mintZ = triangleResult.z; + // AB -> V1V2 + d = findClosestPointsLineSegments(aX, aY, aZ, bX, bY, bZ, v1X, v1Y, v1Z, v2X, v2Y, v2Z, lineSegmentResult, triangleResult); + if (d < min) { + min = d; + minlsX = lineSegmentResult.x; minlsY = lineSegmentResult.y; minlsZ = lineSegmentResult.z; + mintX = triangleResult.x; mintY = triangleResult.y; mintZ = triangleResult.z; + } + // AB -> V2V0 + d = findClosestPointsLineSegments(aX, aY, aZ, bX, bY, bZ, v2X, v2Y, v2Z, v0X, v0Y, v0Z, lineSegmentResult, triangleResult); + if (d < min) { + min = d; + minlsX = lineSegmentResult.x; minlsY = lineSegmentResult.y; minlsZ = lineSegmentResult.z; + mintX = triangleResult.x; mintY = triangleResult.y; mintZ = triangleResult.z; + } + // segment endpoint A and plane of triangle (when A projects inside V0V1V2) + boolean computed = false; + double a = Double.NaN, b = Double.NaN, c = Double.NaN, nd = Double.NaN; + if (testPointInTriangle(aX, aY, aZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z)) { + double v1Y0Y = v1Y - v0Y; + double v2Z0Z = v2Z - v0Z; + double v2Y0Y = v2Y - v0Y; + double v1Z0Z = v1Z - v0Z; + double v2X0X = v2X - v0X; + double v1X0X = v1X - v0X; + a = v1Y0Y * v2Z0Z - v2Y0Y * v1Z0Z; + b = v1Z0Z * v2X0X - v2Z0Z * v1X0X; + c = v1X0X * v2Y0Y - v2X0X * v1Y0Y; + computed = true; + double invLen = Math.invsqrt(a*a + b*b + c*c); + a *= invLen; b *= invLen; c *= invLen; + nd = -(a * v0X + b * v0Y + c * v0Z); + d = (a * aX + b * aY + c * aZ + nd); + double l = d; + d *= d; + if (d < min) { + min = d; + minlsX = aX; minlsY = aY; minlsZ = aZ; + mintX = aX - a*l; mintY = aY - b*l; mintZ = aZ - c*l; + } + } + // segment endpoint B and plane of triangle (when B projects inside V0V1V2) + if (testPointInTriangle(bX, bY, bZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z)) { + if (!computed) { + double v1Y0Y = v1Y - v0Y; + double v2Z0Z = v2Z - v0Z; + double v2Y0Y = v2Y - v0Y; + double v1Z0Z = v1Z - v0Z; + double v2X0X = v2X - v0X; + double v1X0X = v1X - v0X; + a = v1Y0Y * v2Z0Z - v2Y0Y * v1Z0Z; + b = v1Z0Z * v2X0X - v2Z0Z * v1X0X; + c = v1X0X * v2Y0Y - v2X0X * v1Y0Y; + double invLen = Math.invsqrt(a*a + b*b + c*c); + a *= invLen; b *= invLen; c *= invLen; + nd = -(a * v0X + b * v0Y + c * v0Z); + } + d = (a * bX + b * bY + c * bZ + nd); + double l = d; + d *= d; + if (d < min) { + min = d; + minlsX = bX; minlsY = bY; minlsZ = bZ; + mintX = bX - a*l; mintY = bY - b*l; mintZ = bZ - c*l; + } + } + lineSegmentResult.set(minlsX, minlsY, minlsZ); + triangleResult.set(mintX, mintY, mintZ); + return min; + } + + /** + * Determine the closest point on the triangle with the given vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z), (v2X, v2Y, v2Z) + * between that triangle and the given point (pX, pY, pZ) and store that point into the given result. + *

+ * Additionally, this method returns whether the closest point is a vertex ({@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}) + * of the triangle, lies on an edge ({@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20}) + * or on the {@link #POINT_ON_TRIANGLE_FACE face} of the triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.5 "Closest Point on Triangle to Point" + * + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v0Z + * the z coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v1Z + * the z coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @param v2Z + * the z coordinate of the third vertex of the triangle + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the y coordinate of the point + * @param result + * will hold the closest point + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} + */ + public static int findClosestPointOnTriangle( + double v0X, double v0Y, double v0Z, + double v1X, double v1Y, double v1Z, + double v2X, double v2Y, double v2Z, + double pX, double pY, double pZ, + Vector3d result) { + double abX = v1X - v0X, abY = v1Y - v0Y, abZ = v1Z - v0Z; + double acX = v2X - v0X, acY = v2Y - v0Y, acZ = v2Z - v0Z; + double apX = pX - v0X, apY = pY - v0Y, apZ = pZ - v0Z; + double d1 = abX * apX + abY * apY + abZ * apZ; + double d2 = acX * apX + acY * apY + acZ * apZ; + if (d1 <= 0.0 && d2 <= 0.0) { + result.x = v0X; + result.y = v0Y; + result.z = v0Z; + return POINT_ON_TRIANGLE_VERTEX_0; + } + double bpX = pX - v1X, bpY = pY - v1Y, bpZ = pZ - v1Z; + double d3 = abX * bpX + abY * bpY + abZ * bpZ; + double d4 = acX * bpX + acY * bpY + acZ * bpZ; + if (d3 >= 0.0 && d4 <= d3) { + result.x = v1X; + result.y = v1Y; + result.z = v1Z; + return POINT_ON_TRIANGLE_VERTEX_1; + } + double vc = d1 * d4 - d3 * d2; + if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) { + double v = d1 / (d1 - d3); + result.x = v0X + v * abX; + result.y = v0Y + v * abY; + result.z = v0Z + v * abZ; + return POINT_ON_TRIANGLE_EDGE_01; + } + double cpX = pX - v2X, cpY = pY - v2Y, cpZ = pZ - v2Z; + double d5 = abX * cpX + abY * cpY + abZ * cpZ; + double d6 = acX * cpX + acY * cpY + acZ * cpZ; + if (d6 >= 0.0 && d5 <= d6) { + result.x = v2X; + result.y = v2Y; + result.z = v2Z; + return POINT_ON_TRIANGLE_VERTEX_2; + } + double vb = d5 * d2 - d1 * d6; + if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) { + double w = d2 / (d2 - d6); + result.x = v0X + w * acX; + result.y = v0Y + w * acY; + result.z = v0Z + w * acZ; + return POINT_ON_TRIANGLE_EDGE_20; + } + double va = d3 * d6 - d5 * d4; + if (va <= 0.0 && d4 - d3 >= 0.0 && d5 - d6 >= 0.0) { + double w = (d4 - d3) / (d4 - d3 + d5 - d6); + result.x = v1X + w * (v2X - v1X); + result.y = v1Y + w * (v2Y - v1Y); + result.z = v1Z + w * (v2Z - v1Z); + return POINT_ON_TRIANGLE_EDGE_12; + } + double denom = 1.0 / (va + vb + vc); + double v = vb * denom; + double w = vc * denom; + result.x = v0X + abX * v + acX * w; + result.y = v0Y + abY * v + acY * w; + result.z = v0Z + abZ * v + acZ * w; + return POINT_ON_TRIANGLE_FACE; + } + + /** + * Determine the closest point on the triangle with the vertices v0, v1, v2 + * between that triangle and the given point p and store that point into the given result. + *

+ * Additionally, this method returns whether the closest point is a vertex ({@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}) + * of the triangle, lies on an edge ({@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20}) + * or on the {@link #POINT_ON_TRIANGLE_FACE face} of the triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.5 "Closest Point on Triangle to Point" + * + * @param v0 + * the first vertex of the triangle + * @param v1 + * the second vertex of the triangle + * @param v2 + * the third vertex of the triangle + * @param p + * the point + * @param result + * will hold the closest point + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} + */ + public static int findClosestPointOnTriangle(Vector3dc v0, Vector3dc v1, Vector3dc v2, Vector3dc p, Vector3d result) { + return findClosestPointOnTriangle(v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), p.x(), p.y(), p.z(), result); + } + + /** + * Find the point on a given rectangle, specified via three of its corners, which is closest to the specified point + * (pX, pY, pZ) and store the result into res. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.4.2 "Closest Point on 3D Rectangle to Point" + * + * @param aX + * the x coordinate of the first corner point of the rectangle + * @param aY + * the y coordinate of the first corner point of the rectangle + * @param aZ + * the z coordinate of the first corner point of the rectangle + * @param bX + * the x coordinate of the second corner point of the rectangle + * @param bY + * the y coordinate of the second corner point of the rectangle + * @param bZ + * the z coordinate of the second corner point of the rectangle + * @param cX + * the x coordinate of the third corner point of the rectangle + * @param cY + * the y coordinate of the third corner point of the rectangle + * @param cZ + * the z coordinate of the third corner point of the rectangle + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the z coordinate of the point + * @param res + * will hold the result + * @return res + */ + public static Vector3d findClosestPointOnRectangle( + double aX, double aY, double aZ, + double bX, double bY, double bZ, + double cX, double cY, double cZ, + double pX, double pY, double pZ, Vector3d res) { + double abX = bX - aX, abY = bY - aY, abZ = bZ - aZ; + double acX = cX - aX, acY = cY - aY, acZ = cZ - aZ; + double dX = pX - aX, dY = pY - aY, dZ = pZ - aZ; + double qX = aX, qY = aY, qZ = aZ; + double dist = dX * abX + dY * abY + dZ * abZ; + double maxdist = abX * abX + abY * abY + abZ * abZ; + if (dist >= maxdist) { + qX += abX; + qY += abY; + qZ += abZ; + } else if (dist > 0.0) { + qX += (dist / maxdist) * abX; + qY += (dist / maxdist) * abY; + qZ += (dist / maxdist) * abZ; + } + dist = dX * acX + dY * acY + dZ * acZ; + maxdist = acX * acX + acY * acY + acZ * acZ; + if (dist >= maxdist) { + qX += acX; + qY += acY; + qZ += acZ; + } else if (dist > 0.0) { + qX += (dist / maxdist) * acX; + qY += (dist / maxdist) * acY; + qZ += (dist / maxdist) * acZ; + } + res.x = qX; + res.y = qY; + res.z = qZ; + return res; + } + + /** + * Determine the point of intersection between a sphere with the given center (centerX, centerY, centerZ) and radius moving + * with the given velocity (velX, velY, velZ) and the triangle specified via its three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z), (v2X, v2Y, v2Z). + *

+ * The vertices of the triangle must be specified in counter-clockwise winding order. + *

+ * An intersection is only considered if the time of intersection is smaller than the given maxT value. + *

+ * Reference: Improved Collision detection and Response + * + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radius + * the radius of the sphere + * @param velX + * the x component of the velocity of the sphere + * @param velY + * the y component of the velocity of the sphere + * @param velZ + * the z component of the velocity of the sphere + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param v0Z + * the z coordinate of the first triangle vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param v1Z + * the z coordinate of the second triangle vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param v2Z + * the z coordinate of the third triangle vertex + * @param epsilon + * a small epsilon when testing spheres that move almost parallel to the triangle + * @param maxT + * the maximum intersection time + * @param pointAndTime + * iff the moving sphere and the triangle intersect, this will hold the point of intersection in the (x, y, z) components + * and the time of intersection in the w component + * @return {@link #POINT_ON_TRIANGLE_FACE} if the intersection point lies on the triangle's face, + * or {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1} or {@link #POINT_ON_TRIANGLE_VERTEX_2} if the intersection point is a vertex, + * or {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12} or {@link #POINT_ON_TRIANGLE_EDGE_20} if the intersection point lies on an edge; + * or 0 if no intersection + */ + public static int intersectSweptSphereTriangle( + double centerX, double centerY, double centerZ, double radius, double velX, double velY, double velZ, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z, + double epsilon, double maxT, Vector4d pointAndTime) { + double v10X = v1X - v0X; + double v10Y = v1Y - v0Y; + double v10Z = v1Z - v0Z; + double v20X = v2X - v0X; + double v20Y = v2Y - v0Y; + double v20Z = v2Z - v0Z; + // build triangle plane + double a = v10Y * v20Z - v20Y * v10Z; + double b = v10Z * v20X - v20Z * v10X; + double c = v10X * v20Y - v20X * v10Y; + double d = -(a * v0X + b * v0Y + c * v0Z); + double invLen = Math.invsqrt(a * a + b * b + c * c); + double signedDist = (a * centerX + b * centerY + c * centerZ + d) * invLen; + double dot = (a * velX + b * velY + c * velZ) * invLen; + if (dot < epsilon && dot > -epsilon) + return 0; + double pt0 = (radius - signedDist) / dot; + if (pt0 > maxT) + return 0; + double pt1 = (-radius - signedDist) / dot; + double p0X = centerX - radius * a * invLen + velX * pt0; + double p0Y = centerY - radius * b * invLen + velY * pt0; + double p0Z = centerZ - radius * c * invLen + velZ * pt0; + boolean insideTriangle = testPointInTriangle(p0X, p0Y, p0Z, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z); + if (insideTriangle) { + pointAndTime.x = p0X; + pointAndTime.y = p0Y; + pointAndTime.z = p0Z; + pointAndTime.w = pt0; + return POINT_ON_TRIANGLE_FACE; + } + int isect = 0; + double t0 = maxT; + double A = velX * velX + velY * velY + velZ * velZ; + double radius2 = radius * radius; + // test against v0 + double centerV0X = centerX - v0X; + double centerV0Y = centerY - v0Y; + double centerV0Z = centerZ - v0Z; + double B0 = 2.0 * (velX * centerV0X + velY * centerV0Y + velZ * centerV0Z); + double C0 = centerV0X * centerV0X + centerV0Y * centerV0Y + centerV0Z * centerV0Z - radius2; + double root0 = computeLowestRoot(A, B0, C0, t0); + if (root0 < t0) { + pointAndTime.x = v0X; + pointAndTime.y = v0Y; + pointAndTime.z = v0Z; + pointAndTime.w = root0; + t0 = root0; + isect = POINT_ON_TRIANGLE_VERTEX_0; + } + // test against v1 + double centerV1X = centerX - v1X; + double centerV1Y = centerY - v1Y; + double centerV1Z = centerZ - v1Z; + double centerV1Len = centerV1X * centerV1X + centerV1Y * centerV1Y + centerV1Z * centerV1Z; + double B1 = 2.0 * (velX * centerV1X + velY * centerV1Y + velZ * centerV1Z); + double C1 = centerV1Len - radius2; + double root1 = computeLowestRoot(A, B1, C1, t0); + if (root1 < t0) { + pointAndTime.x = v1X; + pointAndTime.y = v1Y; + pointAndTime.z = v1Z; + pointAndTime.w = root1; + t0 = root1; + isect = POINT_ON_TRIANGLE_VERTEX_1; + } + // test against v2 + double centerV2X = centerX - v2X; + double centerV2Y = centerY - v2Y; + double centerV2Z = centerZ - v2Z; + double B2 = 2.0 * (velX * centerV2X + velY * centerV2Y + velZ * centerV2Z); + double C2 = centerV2X * centerV2X + centerV2Y * centerV2Y + centerV2Z * centerV2Z - radius2; + double root2 = computeLowestRoot(A, B2, C2, t0); + if (root2 < t0) { + pointAndTime.x = v2X; + pointAndTime.y = v2Y; + pointAndTime.z = v2Z; + pointAndTime.w = root2; + t0 = root2; + isect = POINT_ON_TRIANGLE_VERTEX_2; + } + double velLen = velX * velX + velY * velY + velZ * velZ; + // test against edge10 + double len10 = v10X * v10X + v10Y * v10Y + v10Z * v10Z; + double baseTo0Len = centerV0X * centerV0X + centerV0Y * centerV0Y + centerV0Z * centerV0Z; + double v10Vel = (v10X * velX + v10Y * velY + v10Z * velZ); + double A10 = len10 * -velLen + v10Vel * v10Vel; + double v10BaseTo0 = v10X * -centerV0X + v10Y * -centerV0Y + v10Z * -centerV0Z; + double velBaseTo0 = velX * -centerV0X + velY * -centerV0Y + velZ * -centerV0Z; + double B10 = len10 * 2 * velBaseTo0 - 2 * v10Vel * v10BaseTo0; + double C10 = len10 * (radius2 - baseTo0Len) + v10BaseTo0 * v10BaseTo0; + double root10 = computeLowestRoot(A10, B10, C10, t0); + double f10 = (v10Vel * root10 - v10BaseTo0) / len10; + if (f10 >= 0.0 && f10 <= 1.0 && root10 < t0) { + pointAndTime.x = v0X + f10 * v10X; + pointAndTime.y = v0Y + f10 * v10Y; + pointAndTime.z = v0Z + f10 * v10Z; + pointAndTime.w = root10; + t0 = root10; + isect = POINT_ON_TRIANGLE_EDGE_01; + } + // test against edge20 + double len20 = v20X * v20X + v20Y * v20Y + v20Z * v20Z; + double v20Vel = (v20X * velX + v20Y * velY + v20Z * velZ); + double A20 = len20 * -velLen + v20Vel * v20Vel; + double v20BaseTo0 = v20X * -centerV0X + v20Y * -centerV0Y + v20Z * -centerV0Z; + double B20 = len20 * 2 * velBaseTo0 - 2 * v20Vel * v20BaseTo0; + double C20 = len20 * (radius2 - baseTo0Len) + v20BaseTo0 * v20BaseTo0; + double root20 = computeLowestRoot(A20, B20, C20, t0); + double f20 = (v20Vel * root20 - v20BaseTo0) / len20; + if (f20 >= 0.0 && f20 <= 1.0 && root20 < pt1) { + pointAndTime.x = v0X + f20 * v20X; + pointAndTime.y = v0Y + f20 * v20Y; + pointAndTime.z = v0Z + f20 * v20Z; + pointAndTime.w = root20; + t0 = root20; + isect = POINT_ON_TRIANGLE_EDGE_20; + } + // test against edge21 + double v21X = v2X - v1X; + double v21Y = v2Y - v1Y; + double v21Z = v2Z - v1Z; + double len21 = v21X * v21X + v21Y * v21Y + v21Z * v21Z; + double baseTo1Len = centerV1Len; + double v21Vel = (v21X * velX + v21Y * velY + v21Z * velZ); + double A21 = len21 * -velLen + v21Vel * v21Vel; + double v21BaseTo1 = v21X * -centerV1X + v21Y * -centerV1Y + v21Z * -centerV1Z; + double velBaseTo1 = velX * -centerV1X + velY * -centerV1Y + velZ * -centerV1Z; + double B21 = len21 * 2 * velBaseTo1 - 2 * v21Vel * v21BaseTo1; + double C21 = len21 * (radius2 - baseTo1Len) + v21BaseTo1 * v21BaseTo1; + double root21 = computeLowestRoot(A21, B21, C21, t0); + double f21 = (v21Vel * root21 - v21BaseTo1) / len21; + if (f21 >= 0.0 && f21 <= 1.0 && root21 < t0) { + pointAndTime.x = v1X + f21 * v21X; + pointAndTime.y = v1Y + f21 * v21Y; + pointAndTime.z = v1Z + f21 * v21Z; + pointAndTime.w = root21; + t0 = root21; + isect = POINT_ON_TRIANGLE_EDGE_12; + } + return isect; + } + + /** + * Compute the lowest root for t in the quadratic equation a*t*t + b*t + c = 0. + *

+ * This is a helper method for {@link #intersectSweptSphereTriangle} + * + * @param a + * the quadratic factor + * @param b + * the linear factor + * @param c + * the constant + * @param maxR + * the maximum expected root + * @return the lowest of the two roots of the quadratic equation; or {@link Double#POSITIVE_INFINITY} + */ + private static double computeLowestRoot(double a, double b, double c, double maxR) { + double determinant = b * b - 4.0 * a * c; + if (determinant < 0.0) + return Double.POSITIVE_INFINITY; + double sqrtD = Math.sqrt(determinant); + double r1 = (-b - sqrtD) / (2.0 * a); + double r2 = (-b + sqrtD) / (2.0 * a); + if (r1 > r2) { + double temp = r2; + r2 = r1; + r1 = temp; + } + if (r1 > 0.0 && r1 < maxR) { + return r1; + } + if (r2 > 0.0 && r2 < maxR) { + return r2; + } + return Double.POSITIVE_INFINITY; + } + + /** + * Test whether the projection of the given point (pX, pY, pZ) lies inside of the triangle defined by the three vertices + * (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z). + *

+ * Reference: Improved Collision detection and Response + * + * @param pX + * the x coordinate of the point to test + * @param pY + * the y coordinate of the point to test + * @param pZ + * the z coordinate of the point to test + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @return true if the projection of the given point lies inside of the given triangle; false otherwise + */ + public static boolean testPointInTriangle(double pX, double pY, double pZ, double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z) { + double e10X = v1X - v0X; + double e10Y = v1Y - v0Y; + double e10Z = v1Z - v0Z; + double e20X = v2X - v0X; + double e20Y = v2Y - v0Y; + double e20Z = v2Z - v0Z; + double a = e10X * e10X + e10Y * e10Y + e10Z * e10Z; + double b = e10X * e20X + e10Y * e20Y + e10Z * e20Z; + double c = e20X * e20X + e20Y * e20Y + e20Z * e20Z; + double ac_bb = a * c - b * b; + double vpX = pX - v0X; + double vpY = pY - v0Y; + double vpZ = pZ - v0Z; + double d = vpX * e10X + vpY * e10Y + vpZ * e10Z; + double e = vpX * e20X + vpY * e20Y + vpZ * e20Z; + double x = d * c - e * b; + double y = e * a - d * b; + double z = x + y - ac_bb; + return ((Runtime.doubleToLongBits(z) & ~(Runtime.doubleToLongBits(x) | Runtime.doubleToLongBits(y))) & 0x8000000000000000L) != 0L; + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and normalized direction (dirX, dirY, dirZ) + * intersects the given sphere with center (centerX, centerY, centerZ) and square radius radiusSquared, + * and store the values of the parameter t in the ray equation p(t) = origin + t * dir for both points (near + * and far) of intersections into the given result vector. + *

+ * This method returns true for a ray whose origin lies inside the sphere. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's normalized direction + * @param dirY + * the y coordinate of the ray's normalized direction + * @param dirZ + * the z coordinate of the ray's normalized direction + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radiusSquared + * the sphere radius squared + * @param result + * a vector that will contain the values of the parameter t in the ray equation + * p(t) = origin + t * dir for both points (near, far) of intersections with the sphere + * @return true if the ray intersects the sphere; false otherwise + */ + public static boolean intersectRaySphere(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double centerX, double centerY, double centerZ, double radiusSquared, Vector2d result) { + double Lx = centerX - originX; + double Ly = centerY - originY; + double Lz = centerZ - originZ; + double tca = Lx * dirX + Ly * dirY + Lz * dirZ; + double d2 = Lx * Lx + Ly * Ly + Lz * Lz - tca * tca; + if (d2 > radiusSquared) + return false; + double thc = Math.sqrt(radiusSquared - d2); + double t0 = tca - thc; + double t1 = tca + thc; + if (t0 < t1 && t1 >= 0.0) { + result.x = t0; + result.y = t1; + return true; + } + return false; + } + + /** + * Test whether the ray with the given origin and normalized direction dir + * intersects the sphere with the given center and square radius radiusSquared, + * and store the values of the parameter t in the ray equation p(t) = origin + t * dir for both points (near + * and far) of intersections into the given result vector. + *

+ * This method returns true for a ray whose origin lies inside the sphere. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's normalized direction + * @param center + * the sphere's center + * @param radiusSquared + * the sphere radius squared + * @param result + * a vector that will contain the values of the parameter t in the ray equation + * p(t) = origin + t * dir for both points (near, far) of intersections with the sphere + * @return true if the ray intersects the sphere; false otherwise + */ + public static boolean intersectRaySphere(Vector3dc origin, Vector3dc dir, Vector3dc center, double radiusSquared, Vector2d result) { + return intersectRaySphere(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), center.x(), center.y(), center.z(), radiusSquared, result); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and normalized direction (dirX, dirY, dirZ) + * intersects the given sphere with center (centerX, centerY, centerZ) and square radius radiusSquared. + *

+ * This method returns true for a ray whose origin lies inside the sphere. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's normalized direction + * @param dirY + * the y coordinate of the ray's normalized direction + * @param dirZ + * the z coordinate of the ray's normalized direction + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radiusSquared + * the sphere radius squared + * @return true if the ray intersects the sphere; false otherwise + */ + public static boolean testRaySphere(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double centerX, double centerY, double centerZ, double radiusSquared) { + double Lx = centerX - originX; + double Ly = centerY - originY; + double Lz = centerZ - originZ; + double tca = Lx * dirX + Ly * dirY + Lz * dirZ; + double d2 = Lx * Lx + Ly * Ly + Lz * Lz - tca * tca; + if (d2 > radiusSquared) + return false; + double thc = Math.sqrt(radiusSquared - d2); + double t0 = tca - thc; + double t1 = tca + thc; + return t0 < t1 && t1 >= 0.0; + } + + /** + * Test whether the ray with the given origin and normalized direction dir + * intersects the sphere with the given center and square radius. + *

+ * This method returns true for a ray whose origin lies inside the sphere. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's normalized direction + * @param center + * the sphere's center + * @param radiusSquared + * the sphere radius squared + * @return true if the ray intersects the sphere; false otherwise + */ + public static boolean testRaySphere(Vector3dc origin, Vector3dc dir, Vector3dc center, double radiusSquared) { + return testRaySphere(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), center.x(), center.y(), center.z(), radiusSquared); + } + + /** + * Test whether the line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the given sphere with center (centerX, centerY, centerZ) and square radius radiusSquared. + *

+ * Reference: http://paulbourke.net/ + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radiusSquared + * the sphere radius squared + * @return true if the line segment intersects the sphere; false otherwise + */ + public static boolean testLineSegmentSphere(double p0X, double p0Y, double p0Z, double p1X, double p1Y, double p1Z, + double centerX, double centerY, double centerZ, double radiusSquared) { + double dX = p1X - p0X; + double dY = p1Y - p0Y; + double dZ = p1Z - p0Z; + double nom = (centerX - p0X) * dX + (centerY - p0Y) * dY + (centerZ - p0Z) * dZ; + double den = dX * dX + dY * dY + dZ * dZ; + double u = nom / den; + if (u < 0.0) { + dX = p0X - centerX; + dY = p0Y - centerY; + dZ = p0Z - centerZ; + } else if (u > 1.0) { + dX = p1X - centerX; + dY = p1Y - centerY; + dZ = p1Z - centerZ; + } else { // has to be >= 0 and <= 1 + double pX = p0X + u * dX; + double pY = p0Y + u * dY; + double pZ = p0Z + u * dZ; + dX = pX - centerX; + dY = pY - centerY; + dZ = pZ - centerZ; + } + double dist = dX * dX + dY * dY + dZ * dZ; + return dist <= radiusSquared; + } + + /** + * Test whether the line segment with the end points p0 and p1 + * intersects the given sphere with center center and square radius radiusSquared. + *

+ * Reference: http://paulbourke.net/ + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param center + * the sphere's center + * @param radiusSquared + * the sphere radius squared + * @return true if the line segment intersects the sphere; false otherwise + */ + public static boolean testLineSegmentSphere(Vector3dc p0, Vector3dc p1, Vector3dc center, double radiusSquared) { + return testLineSegmentSphere(p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z(), center.x(), center.y(), center.z(), radiusSquared); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the axis-aligned box given as its minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ), + * and return the values of the parameter t in the ray equation p(t) = origin + t * dir of the near and far point of intersection. + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned box. + *

+ * If many boxes need to be tested against the same ray, then the {@link RayAabIntersection} class is likely more efficient. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectRayAab(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector2d) + * @see RayAabIntersection + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the y coordinate of the maximum corner of the axis-aligned box + * @param result + * a vector which will hold the resulting values of the parameter + * t in the ray equation p(t) = origin + t * dir of the near and far point of intersection + * iff the ray intersects the axis-aligned box + * @return true if the given ray intersects the axis-aligned box; false otherwise + */ + public static boolean intersectRayAab(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vector2d result) { + double invDirX = 1.0 / dirX, invDirY = 1.0 / dirY, invDirZ = 1.0 / dirZ; + double tNear, tFar, tymin, tymax, tzmin, tzmax; + if (invDirX >= 0.0) { + tNear = (minX - originX) * invDirX; + tFar = (maxX - originX) * invDirX; + } else { + tNear = (maxX - originX) * invDirX; + tFar = (minX - originX) * invDirX; + } + if (invDirY >= 0.0) { + tymin = (minY - originY) * invDirY; + tymax = (maxY - originY) * invDirY; + } else { + tymin = (maxY - originY) * invDirY; + tymax = (minY - originY) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return false; + if (invDirZ >= 0.0) { + tzmin = (minZ - originZ) * invDirZ; + tzmax = (maxZ - originZ) * invDirZ; + } else { + tzmin = (maxZ - originZ) * invDirZ; + tzmax = (minZ - originZ) * invDirZ; + } + if (tNear > tzmax || tzmin > tFar) + return false; + tNear = tymin > tNear || Double.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Double.isNaN(tFar) ? tymax : tFar; + tNear = tzmin > tNear ? tzmin : tNear; + tFar = tzmax < tFar ? tzmax : tFar; + if (tNear < tFar && tFar >= 0.0) { + result.x = tNear; + result.y = tFar; + return true; + } + return false; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the axis-aligned box specified as its minimum corner min and maximum corner max, + * and return the values of the parameter t in the ray equation p(t) = origin + t * dir of the near and far point of intersection.. + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned box. + *

+ * If many boxes need to be tested against the same ray, then the {@link RayAabIntersection} class is likely more efficient. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectRayAab(double, double, double, double, double, double, double, double, double, double, double, double, Vector2d) + * @see RayAabIntersection + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param result + * a vector which will hold the resulting values of the parameter + * t in the ray equation p(t) = origin + t * dir of the near and far point of intersection + * iff the ray intersects the axis-aligned box + * @return true if the given ray intersects the axis-aligned box; false otherwise + */ + public static boolean intersectRayAab(Vector3dc origin, Vector3dc dir, Vector3dc min, Vector3dc max, Vector2d result) { + return intersectRayAab(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), result); + } + + /** + * Determine whether the undirected line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the axis-aligned box given as its minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ), + * and return the values of the parameter t in the ray equation p(t) = origin + p0 * (p1 - p0) of the near and far point of intersection. + *

+ * This method returns true for a line segment whose either end point lies inside the axis-aligned box. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectLineSegmentAab(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector2d) + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param minX + * the x coordinate of one corner of the axis-aligned box + * @param minY + * the y coordinate of one corner of the axis-aligned box + * @param minZ + * the z coordinate of one corner of the axis-aligned box + * @param maxX + * the x coordinate of the opposite corner of the axis-aligned box + * @param maxY + * the y coordinate of the opposite corner of the axis-aligned box + * @param maxZ + * the y coordinate of the opposite corner of the axis-aligned box + * @param result + * a vector which will hold the resulting values of the parameter + * t in the ray equation p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * iff the line segment intersects the axis-aligned box + * @return {@link #INSIDE} if the line segment lies completely inside of the axis-aligned box; or + * {@link #OUTSIDE} if the line segment lies completely outside of the axis-aligned box; or + * {@link #ONE_INTERSECTION} if one of the end points of the line segment lies inside of the axis-aligned box; or + * {@link #TWO_INTERSECTION} if the line segment intersects two sides of the axis-aligned box + * or lies on an edge or a side of the box + */ + public static int intersectLineSegmentAab(double p0X, double p0Y, double p0Z, double p1X, double p1Y, double p1Z, + double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vector2d result) { + double dirX = p1X - p0X, dirY = p1Y - p0Y, dirZ = p1Z - p0Z; + double invDirX = 1.0 / dirX, invDirY = 1.0 / dirY, invDirZ = 1.0 / dirZ; + double tNear, tFar, tymin, tymax, tzmin, tzmax; + if (invDirX >= 0.0) { + tNear = (minX - p0X) * invDirX; + tFar = (maxX - p0X) * invDirX; + } else { + tNear = (maxX - p0X) * invDirX; + tFar = (minX - p0X) * invDirX; + } + if (invDirY >= 0.0) { + tymin = (minY - p0Y) * invDirY; + tymax = (maxY - p0Y) * invDirY; + } else { + tymin = (maxY - p0Y) * invDirY; + tymax = (minY - p0Y) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return OUTSIDE; + if (invDirZ >= 0.0) { + tzmin = (minZ - p0Z) * invDirZ; + tzmax = (maxZ - p0Z) * invDirZ; + } else { + tzmin = (maxZ - p0Z) * invDirZ; + tzmax = (minZ - p0Z) * invDirZ; + } + if (tNear > tzmax || tzmin > tFar) + return OUTSIDE; + tNear = tymin > tNear || Double.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Double.isNaN(tFar) ? tymax : tFar; + tNear = tzmin > tNear ? tzmin : tNear; + tFar = tzmax < tFar ? tzmax : tFar; + int type = OUTSIDE; + if (tNear <= tFar && tNear <= 1.0f && tFar >= 0.0f) { + if (tNear >= 0.0f && tFar > 1.0f) { + tFar = tNear; + type = ONE_INTERSECTION; + } else if (tNear < 0.0f && tFar <= 1.0f) { + tNear = tFar; + type = ONE_INTERSECTION; + } else if (tNear < 0.0f && tFar > 1.0f) { + type = INSIDE; + } else { + type = TWO_INTERSECTION; + } + result.x = tNear; + result.y = tFar; + } + return type; + } + + /** + * Determine whether the undirected line segment with the end points p0 and p1 + * intersects the axis-aligned box given as its minimum corner min and maximum corner max, + * and return the values of the parameter t in the ray equation p(t) = origin + p0 * (p1 - p0) of the near and far point of intersection. + *

+ * This method returns true for a line segment whose either end point lies inside the axis-aligned box. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectLineSegmentAab(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector2d) + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param result + * a vector which will hold the resulting values of the parameter + * t in the ray equation p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * iff the line segment intersects the axis-aligned box + * @return {@link #INSIDE} if the line segment lies completely inside of the axis-aligned box; or + * {@link #OUTSIDE} if the line segment lies completely outside of the axis-aligned box; or + * {@link #ONE_INTERSECTION} if one of the end points of the line segment lies inside of the axis-aligned box; or + * {@link #TWO_INTERSECTION} if the line segment intersects two sides of the axis-aligned box + * or lies on an edge or a side of the box + */ + public static int intersectLineSegmentAab(Vector3dc p0, Vector3dc p1, Vector3dc min, Vector3dc max, Vector2d result) { + return intersectLineSegmentAab(p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z(), min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), result); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the axis-aligned box given as its minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ). + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned box. + *

+ * If many boxes need to be tested against the same ray, then the {@link RayAabIntersection} class is likely more efficient. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #testRayAab(Vector3dc, Vector3dc, Vector3dc, Vector3dc) + * @see RayAabIntersection + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the y coordinate of the maximum corner of the axis-aligned box + * @return true if the given ray intersects the axis-aligned box; false otherwise + */ + public static boolean testRayAab(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + double invDirX = 1.0 / dirX, invDirY = 1.0 / dirY, invDirZ = 1.0 / dirZ; + double tNear, tFar, tymin, tymax, tzmin, tzmax; + if (invDirX >= 0.0) { + tNear = (minX - originX) * invDirX; + tFar = (maxX - originX) * invDirX; + } else { + tNear = (maxX - originX) * invDirX; + tFar = (minX - originX) * invDirX; + } + if (invDirY >= 0.0) { + tymin = (minY - originY) * invDirY; + tymax = (maxY - originY) * invDirY; + } else { + tymin = (maxY - originY) * invDirY; + tymax = (minY - originY) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return false; + if (invDirZ >= 0.0) { + tzmin = (minZ - originZ) * invDirZ; + tzmax = (maxZ - originZ) * invDirZ; + } else { + tzmin = (maxZ - originZ) * invDirZ; + tzmax = (minZ - originZ) * invDirZ; + } + if (tNear > tzmax || tzmin > tFar) + return false; + tNear = tymin > tNear || Double.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Double.isNaN(tFar) ? tymax : tFar; + tNear = tzmin > tNear ? tzmin : tNear; + tFar = tzmax < tFar ? tzmax : tFar; + return tNear < tFar && tFar >= 0.0; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the axis-aligned box specified as its minimum corner min and maximum corner max. + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned box. + *

+ * If many boxes need to be tested against the same ray, then the {@link RayAabIntersection} class is likely more efficient. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #testRayAab(double, double, double, double, double, double, double, double, double, double, double, double) + * @see RayAabIntersection + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @return true if the given ray intersects the axis-aligned box; false otherwise + */ + public static boolean testRayAab(Vector3dc origin, Vector3dc dir, Vector3dc min, Vector3dc max) { + return testRayAab(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), min.x(), min.y(), min.z(), max.x(), max.y(), max.z()); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the frontface of the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z). + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test implements backface culling, that is, it will return false when the triangle is in clockwise + * winding order assuming a right-handed coordinate system when seen along the ray's direction, even if the ray intersects the triangle. + * This is in compliance with how OpenGL handles backface culling with default frontface/backface settings. + * + * @see #testRayTriangleFront(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3dc, double) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return true if the given ray intersects the frontface of the triangle; false otherwise + */ + public static boolean testRayTriangleFront(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z, + double epsilon) { + double edge1X = v1X - v0X; + double edge1Y = v1Y - v0Y; + double edge1Z = v1Z - v0Z; + double edge2X = v2X - v0X; + double edge2Y = v2Y - v0Y; + double edge2Z = v2Z - v0Z; + double pvecX = dirY * edge2Z - dirZ * edge2Y; + double pvecY = dirZ * edge2X - dirX * edge2Z; + double pvecZ = dirX * edge2Y - dirY * edge2X; + double det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det < epsilon) + return false; + double tvecX = originX - v0X; + double tvecY = originY - v0Y; + double tvecZ = originZ - v0Z; + double u = (tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ); + if (u < 0.0 || u > det) + return false; + double qvecX = tvecY * edge1Z - tvecZ * edge1Y; + double qvecY = tvecZ * edge1X - tvecX * edge1Z; + double qvecZ = tvecX * edge1Y - tvecY * edge1X; + double v = (dirX * qvecX + dirY * qvecY + dirZ * qvecZ); + if (v < 0.0 || u + v > det) + return false; + double invDet = 1.0 / det; + double t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t >= epsilon; + } + + /** + * Test whether the ray with the given origin and the given dir intersects the frontface of the triangle consisting of the three vertices + * v0, v1 and v2. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test implements backface culling, that is, it will return false when the triangle is in clockwise + * winding order assuming a right-handed coordinate system when seen along the ray's direction, even if the ray intersects the triangle. + * This is in compliance with how OpenGL handles backface culling with default frontface/backface settings. + * + * @see #testRayTriangleFront(double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return true if the given ray intersects the frontface of the triangle; false otherwise + */ + public static boolean testRayTriangleFront(Vector3dc origin, Vector3dc dir, Vector3dc v0, Vector3dc v1, Vector3dc v2, double epsilon) { + return testRayTriangleFront(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z). + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test does not take into account the winding order of the triangle, so a ray will intersect a front-facing triangle as well as a back-facing triangle. + * + * @see #testRayTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3dc, double) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return true if the given ray intersects the frontface of the triangle; false otherwise + */ + public static boolean testRayTriangle(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z, + double epsilon) { + double edge1X = v1X - v0X; + double edge1Y = v1Y - v0Y; + double edge1Z = v1Z - v0Z; + double edge2X = v2X - v0X; + double edge2Y = v2Y - v0Y; + double edge2Z = v2Z - v0Z; + double pvecX = dirY * edge2Z - dirZ * edge2Y; + double pvecY = dirZ * edge2X - dirX * edge2Z; + double pvecZ = dirX * edge2Y - dirY * edge2X; + double det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det > -epsilon && det < epsilon) + return false; + double tvecX = originX - v0X; + double tvecY = originY - v0Y; + double tvecZ = originZ - v0Z; + double invDet = 1.0 / det; + double u = (tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ) * invDet; + if (u < 0.0 || u > 1.0) + return false; + double qvecX = tvecY * edge1Z - tvecZ * edge1Y; + double qvecY = tvecZ * edge1X - tvecX * edge1Z; + double qvecZ = tvecX * edge1Y - tvecY * edge1X; + double v = (dirX * qvecX + dirY * qvecY + dirZ * qvecZ) * invDet; + if (v < 0.0 || u + v > 1.0) + return false; + double t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t >= epsilon; + } + + /** + * Test whether the ray with the given origin and the given dir intersects the frontface of the triangle consisting of the three vertices + * v0, v1 and v2. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test does not take into account the winding order of the triangle, so a ray will intersect a front-facing triangle as well as a back-facing triangle. + * + * @see #testRayTriangle(double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return true if the given ray intersects the frontface of the triangle; false otherwise + */ + public static boolean testRayTriangle(Vector3dc origin, Vector3dc dir, Vector3dc v0, Vector3dc v1, Vector3dc v2, double epsilon) { + return testRayTriangle(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Determine whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the frontface of the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z) + * and return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test implements backface culling, that is, it will return false when the triangle is in clockwise + * winding order assuming a right-handed coordinate system when seen along the ray's direction, even if the ray intersects the triangle. + * This is in compliance with how OpenGL handles backface culling with default frontface/backface settings. + * + * @see #testRayTriangleFront(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3dc, double) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection + * if the ray intersects the frontface of the triangle; -1.0 otherwise + */ + public static double intersectRayTriangleFront(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z, + double epsilon) { + double edge1X = v1X - v0X; + double edge1Y = v1Y - v0Y; + double edge1Z = v1Z - v0Z; + double edge2X = v2X - v0X; + double edge2Y = v2Y - v0Y; + double edge2Z = v2Z - v0Z; + double pvecX = dirY * edge2Z - dirZ * edge2Y; + double pvecY = dirZ * edge2X - dirX * edge2Z; + double pvecZ = dirX * edge2Y - dirY * edge2X; + double det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det <= epsilon) + return -1.0; + double tvecX = originX - v0X; + double tvecY = originY - v0Y; + double tvecZ = originZ - v0Z; + double u = tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ; + if (u < 0.0 || u > det) + return -1.0; + double qvecX = tvecY * edge1Z - tvecZ * edge1Y; + double qvecY = tvecZ * edge1X - tvecX * edge1Z; + double qvecZ = tvecX * edge1Y - tvecY * edge1X; + double v = dirX * qvecX + dirY * qvecY + dirZ * qvecZ; + if (v < 0.0 || u + v > det) + return -1.0; + double invDet = 1.0 / det; + double t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t; + } + + /** + * Determine whether the ray with the given origin and the given dir intersects the frontface of the triangle consisting of the three vertices + * v0, v1 and v2 and return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test implements backface culling, that is, it will return false when the triangle is in clockwise + * winding order assuming a right-handed coordinate system when seen along the ray's direction, even if the ray intersects the triangle. + * This is in compliance with how OpenGL handles backface culling with default frontface/backface settings. + * + * @see #intersectRayTriangleFront(double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection + * if the ray intersects the frontface of the triangle; -1.0 otherwise + */ + public static double intersectRayTriangleFront(Vector3dc origin, Vector3dc dir, Vector3dc v0, Vector3dc v1, Vector3dc v2, double epsilon) { + return intersectRayTriangleFront(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Determine whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z) + * and return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test does not take into account the winding order of the triangle, so a ray will intersect a front-facing triangle as well as a back-facing triangle. + * + * @see #testRayTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3dc, double) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection + * if the ray intersects the triangle; -1.0 otherwise + */ + public static double intersectRayTriangle(double originX, double originY, double originZ, double dirX, double dirY, double dirZ, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z, + double epsilon) { + double edge1X = v1X - v0X; + double edge1Y = v1Y - v0Y; + double edge1Z = v1Z - v0Z; + double edge2X = v2X - v0X; + double edge2Y = v2Y - v0Y; + double edge2Z = v2Z - v0Z; + double pvecX = dirY * edge2Z - dirZ * edge2Y; + double pvecY = dirZ * edge2X - dirX * edge2Z; + double pvecZ = dirX * edge2Y - dirY * edge2X; + double det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det > -epsilon && det < epsilon) + return -1.0; + double tvecX = originX - v0X; + double tvecY = originY - v0Y; + double tvecZ = originZ - v0Z; + double invDet = 1.0 / det; + double u = (tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ) * invDet; + if (u < 0.0 || u > 1.0) + return -1.0; + double qvecX = tvecY * edge1Z - tvecZ * edge1Y; + double qvecY = tvecZ * edge1X - tvecX * edge1Z; + double qvecZ = tvecX * edge1Y - tvecY * edge1X; + double v = (dirX * qvecX + dirY * qvecY + dirZ * qvecZ) * invDet; + if (v < 0.0 || u + v > 1.0) + return -1.0; + double t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t; + } + + /** + * Determine whether the ray with the given origin and the given dir intersects the triangle consisting of the three vertices + * v0, v1 and v2 and return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test does not take into account the winding order of the triangle, so a ray will intersect a front-facing triangle as well as a back-facing triangle. + * + * @see #intersectRayTriangle(double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection + * if the ray intersects the triangle; -1.0 otherwise + */ + public static double intersectRayTriangle(Vector3dc origin, Vector3dc dir, Vector3dc v0, Vector3dc v1, Vector3dc v2, double epsilon) { + return intersectRayTriangle(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Test whether the line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z), + * regardless of the winding order of the triangle or the direction of the line segment between its two end points. + *

+ * Reference: + * Fast, Minimum Storage Ray/Triangle Intersection + * + * @see #testLineSegmentTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3dc, double) + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing line segments that are almost parallel to the triangle + * @return true if the given line segment intersects the triangle; false otherwise + */ + public static boolean testLineSegmentTriangle(double p0X, double p0Y, double p0Z, double p1X, double p1Y, double p1Z, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z, + double epsilon) { + double dirX = p1X - p0X; + double dirY = p1Y - p0Y; + double dirZ = p1Z - p0Z; + double t = intersectRayTriangle(p0X, p0Y, p0Z, dirX, dirY, dirZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z, epsilon); + return t >= 0.0 && t <= 1.0; + } + + /** + * Test whether the line segment with the end points p0 and p1 + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z), + * regardless of the winding order of the triangle or the direction of the line segment between its two end points. + *

+ * Reference: + * Fast, Minimum Storage Ray/Triangle Intersection + * + * @see #testLineSegmentTriangle(double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing line segments that are almost parallel to the triangle + * @return true if the given line segment intersects the triangle; false otherwise + */ + public static boolean testLineSegmentTriangle(Vector3dc p0, Vector3dc p1, Vector3dc v0, Vector3dc v1, Vector3dc v2, double epsilon) { + return testLineSegmentTriangle(p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Determine whether the line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z), + * regardless of the winding order of the triangle or the direction of the line segment between its two end points, + * and return the point of intersection. + *

+ * Reference: + * Fast, Minimum Storage Ray/Triangle Intersection + * + * @see #intersectLineSegmentTriangle(Vector3dc, Vector3dc, Vector3dc, Vector3dc, Vector3dc, double, Vector3d) + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing line segments that are almost parallel to the triangle + * @param intersectionPoint + * the point of intersection + * @return true if the given line segment intersects the triangle; false otherwise + */ + public static boolean intersectLineSegmentTriangle(double p0X, double p0Y, double p0Z, double p1X, double p1Y, double p1Z, + double v0X, double v0Y, double v0Z, double v1X, double v1Y, double v1Z, double v2X, double v2Y, double v2Z, + double epsilon, Vector3d intersectionPoint) { + double dirX = p1X - p0X; + double dirY = p1Y - p0Y; + double dirZ = p1Z - p0Z; + double t = intersectRayTriangle(p0X, p0Y, p0Z, dirX, dirY, dirZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z, epsilon); + if (t >= 0.0 && t <= 1.0) { + intersectionPoint.x = p0X + dirX * t; + intersectionPoint.y = p0Y + dirY * t; + intersectionPoint.z = p0Z + dirZ * t; + return true; + } + return false; + } + + /** + * Determine whether the line segment with the end points p0 and p1 + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z), + * regardless of the winding order of the triangle or the direction of the line segment between its two end points, + * and return the point of intersection. + *

+ * Reference: + * Fast, Minimum Storage Ray/Triangle Intersection + * + * @see #intersectLineSegmentTriangle(double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, Vector3d) + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing line segments that are almost parallel to the triangle + * @param intersectionPoint + * the point of intersection + * @return true if the given line segment intersects the triangle; false otherwise + */ + public static boolean intersectLineSegmentTriangle(Vector3dc p0, Vector3dc p1, Vector3dc v0, Vector3dc v1, Vector3dc v2, double epsilon, Vector3d intersectionPoint) { + return intersectLineSegmentTriangle(p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon, intersectionPoint); + } + + /** + * Determine whether the line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the plane given as the general plane equation a*x + b*y + c*z + d = 0, + * and return the point of intersection. + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param intersectionPoint + * the point of intersection + * @return true if the given line segment intersects the plane; false otherwise + */ + public static boolean intersectLineSegmentPlane(double p0X, double p0Y, double p0Z, double p1X, double p1Y, double p1Z, + double a, double b, double c, double d, Vector3d intersectionPoint) { + double dirX = p1X - p0X; + double dirY = p1Y - p0Y; + double dirZ = p1Z - p0Z; + double denom = a * dirX + b * dirY + c * dirZ; + double t = -(a * p0X + b * p0Y + c * p0Z + d) / denom; + if (t >= 0.0 && t <= 1.0) { + intersectionPoint.x = p0X + t * dirX; + intersectionPoint.y = p0Y + t * dirY; + intersectionPoint.z = p0Z + t * dirZ; + return true; + } + return false; + } + + /** + * Test whether the line with the general line equation a*x + b*y + c = 0 intersects the circle with center + * (centerX, centerY) and radius. + *

+ * Reference: http://math.stackexchange.com + * + * @param a + * the x factor in the line equation + * @param b + * the y factor in the line equation + * @param c + * the constant in the line equation + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radius + * the radius of the circle + * @return true iff the line intersects the circle; false otherwise + */ + public static boolean testLineCircle(double a, double b, double c, double centerX, double centerY, double radius) { + double denom = Math.sqrt(a * a + b * b); + double dist = (a * centerX + b * centerY + c) / denom; + return -radius <= dist && dist <= radius; + } + + /** + * Test whether the line with the general line equation a*x + b*y + c = 0 intersects the circle with center + * (centerX, centerY) and radius, and store the center of the line segment of + * intersection in the (x, y) components of the supplied vector and the half-length of that line segment in the z component. + *

+ * Reference: http://math.stackexchange.com + * + * @param a + * the x factor in the line equation + * @param b + * the y factor in the line equation + * @param c + * the constant in the line equation + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radius + * the radius of the circle + * @param intersectionCenterAndHL + * will hold the center of the line segment of intersection in the (x, y) components and the half-length in the z component + * @return true iff the line intersects the circle; false otherwise + */ + public static boolean intersectLineCircle(double a, double b, double c, double centerX, double centerY, double radius, Vector3d intersectionCenterAndHL) { + double invDenom = Math.invsqrt(a * a + b * b); + double dist = (a * centerX + b * centerY + c) * invDenom; + if (-radius <= dist && dist <= radius) { + intersectionCenterAndHL.x = centerX + dist * a * invDenom; + intersectionCenterAndHL.y = centerY + dist * b * invDenom; + intersectionCenterAndHL.z = Math.sqrt(radius * radius - dist * dist); + return true; + } + return false; + } + + /** + * Test whether the line defined by the two points (x0, y0) and (x1, y1) intersects the circle with center + * (centerX, centerY) and radius, and store the center of the line segment of + * intersection in the (x, y) components of the supplied vector and the half-length of that line segment in the z component. + *

+ * Reference: http://math.stackexchange.com + * + * @param x0 + * the x coordinate of the first point on the line + * @param y0 + * the y coordinate of the first point on the line + * @param x1 + * the x coordinate of the second point on the line + * @param y1 + * the y coordinate of the second point on the line + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radius + * the radius of the circle + * @param intersectionCenterAndHL + * will hold the center of the line segment of intersection in the (x, y) components and the half-length in the z component + * @return true iff the line intersects the circle; false otherwise + */ + public static boolean intersectLineCircle(double x0, double y0, double x1, double y1, double centerX, double centerY, double radius, Vector3d intersectionCenterAndHL) { + // Build general line equation from two points and use the other method + return intersectLineCircle(y0 - y1, x1 - x0, (x0 - x1) * y0 + (y1 - y0) * x0, centerX, centerY, radius, intersectionCenterAndHL); + } + + /** + * Test whether the axis-aligned rectangle with minimum corner (minX, minY) and maximum corner (maxX, maxY) + * intersects the line with the general equation a*x + b*y + c = 0. + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param a + * the x factor in the line equation + * @param b + * the y factor in the line equation + * @param c + * the constant in the plane equation + * @return true iff the axis-aligned rectangle intersects the line; false otherwise + */ + public static boolean testAarLine(double minX, double minY, double maxX, double maxY, double a, double b, double c) { + double pX, pY, nX, nY; + if (a > 0.0) { + pX = maxX; + nX = minX; + } else { + pX = minX; + nX = maxX; + } + if (b > 0.0) { + pY = maxY; + nY = minY; + } else { + pY = minY; + nY = maxY; + } + double distN = c + a * nX + b * nY; + double distP = c + a * pX + b * pY; + return distN <= 0.0 && distP >= 0.0; + } + + /** + * Test whether the axis-aligned rectangle with minimum corner min and maximum corner max + * intersects the line with the general equation a*x + b*y + c = 0. + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @param a + * the x factor in the line equation + * @param b + * the y factor in the line equation + * @param c + * the constant in the line equation + * @return true iff the axis-aligned rectangle intersects the line; false otherwise + */ + public static boolean testAarLine(Vector2dc min, Vector2dc max, double a, double b, double c) { + return testAarLine(min.x(), min.y(), max.x(), max.y(), a, b, c); + } + + /** + * Test whether the axis-aligned rectangle with minimum corner (minX, minY) and maximum corner (maxX, maxY) + * intersects the line defined by the two points (x0, y0) and (x1, y1). + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param x0 + * the x coordinate of the first point on the line + * @param y0 + * the y coordinate of the first point on the line + * @param x1 + * the x coordinate of the second point on the line + * @param y1 + * the y coordinate of the second point on the line + * @return true iff the axis-aligned rectangle intersects the line; false otherwise + */ + public static boolean testAarLine(double minX, double minY, double maxX, double maxY, double x0, double y0, double x1, double y1) { + double a = y0 - y1; + double b = x1 - x0; + double c = -b * y0 - a * x0; + return testAarLine(minX, minY, maxX, maxY, a, b, c); + } + + /** + * Test whether the axis-aligned rectangle with minimum corner (minXA, minYA) and maximum corner (maxXA, maxYA) + * intersects the axis-aligned rectangle with minimum corner (minXB, minYB) and maximum corner (maxXB, maxYB). + * + * @param minXA + * the x coordinate of the minimum corner of the first axis-aligned rectangle + * @param minYA + * the y coordinate of the minimum corner of the first axis-aligned rectangle + * @param maxXA + * the x coordinate of the maximum corner of the first axis-aligned rectangle + * @param maxYA + * the y coordinate of the maximum corner of the first axis-aligned rectangle + * @param minXB + * the x coordinate of the minimum corner of the second axis-aligned rectangle + * @param minYB + * the y coordinate of the minimum corner of the second axis-aligned rectangle + * @param maxXB + * the x coordinate of the maximum corner of the second axis-aligned rectangle + * @param maxYB + * the y coordinate of the maximum corner of the second axis-aligned rectangle + * @return true iff both axis-aligned rectangles intersect; false otherwise + */ + public static boolean testAarAar(double minXA, double minYA, double maxXA, double maxYA, double minXB, double minYB, double maxXB, double maxYB) { + return maxXA >= minXB && maxYA >= minYB && minXA <= maxXB && minYA <= maxYB; + } + + /** + * Test whether the axis-aligned rectangle with minimum corner minA and maximum corner maxA + * intersects the axis-aligned rectangle with minimum corner minB and maximum corner maxB. + * + * @param minA + * the minimum corner of the first axis-aligned rectangle + * @param maxA + * the maximum corner of the first axis-aligned rectangle + * @param minB + * the minimum corner of the second axis-aligned rectangle + * @param maxB + * the maximum corner of the second axis-aligned rectangle + * @return true iff both axis-aligned rectangles intersect; false otherwise + */ + public static boolean testAarAar(Vector2dc minA, Vector2dc maxA, Vector2dc minB, Vector2dc maxB) { + return testAarAar(minA.x(), minA.y(), maxA.x(), maxA.y(), minB.x(), minB.y(), maxB.x(), maxB.y()); + } + + /** + * Test whether a given circle with center (aX, aY) and radius aR and travelled distance vector (maX, maY) + * intersects a given static circle with center (bX, bY) and radius bR. + *

+ * Note that the case of two moving circles can always be reduced to this case by expressing the moved distance of one of the circles relative + * to the other. + *

+ * Reference: https://www.gamasutra.com + * + * @param aX + * the x coordinate of the first circle's center + * @param aY + * the y coordinate of the first circle's center + * @param maX + * the x coordinate of the first circle's travelled distance vector + * @param maY + * the y coordinate of the first circle's travelled distance vector + * @param aR + * the radius of the first circle + * @param bX + * the x coordinate of the second circle's center + * @param bY + * the y coordinate of the second circle's center + * @param bR + * the radius of the second circle + * @return true if both circle intersect; false otherwise + */ + public static boolean testMovingCircleCircle(double aX, double aY, double maX, double maY, double aR, double bX, double bY, double bR) { + double aRbR = aR + bR; + double dist = Math.sqrt((aX - bX) * (aX - bX) + (aY - bY) * (aY - bY)) - aRbR; + double mLen = Math.sqrt(maX * maX + maY * maY); + if (mLen < dist) + return false; + double invMLen = 1.0 / mLen; + double nX = maX * invMLen; + double nY = maY * invMLen; + double cX = bX - aX; + double cY = bY - aY; + double nDotC = nX * cX + nY * cY; + if (nDotC <= 0.0) + return false; + double cLen = Math.sqrt(cX * cX + cY * cY); + double cLenNdotC = cLen * cLen - nDotC * nDotC; + double aRbR2 = aRbR * aRbR; + if (cLenNdotC >= aRbR2) + return false; + double t = aRbR2 - cLenNdotC; + if (t < 0.0) + return false; + double distance = nDotC - Math.sqrt(t); + double mag = mLen; + if (mag < distance) + return false; + return true; + } + + /** + * Test whether a given circle with center centerA and radius aR and travelled distance vector moveA + * intersects a given static circle with center centerB and radius bR. + *

+ * Note that the case of two moving circles can always be reduced to this case by expressing the moved distance of one of the circles relative + * to the other. + *

+ * Reference: https://www.gamasutra.com + * + * @param centerA + * the coordinates of the first circle's center + * @param moveA + * the coordinates of the first circle's travelled distance vector + * @param aR + * the radius of the first circle + * @param centerB + * the coordinates of the second circle's center + * @param bR + * the radius of the second circle + * @return true if both circle intersect; false otherwise + */ + public static boolean testMovingCircleCircle(Vector2d centerA, Vector2d moveA, double aR, Vector2d centerB, double bR) { + return testMovingCircleCircle(centerA.x, centerA.y, moveA.x, moveA.y, aR, centerB.x, centerB.y, bR); + } + + /** + * Test whether the one circle with center (aX, aY) and square radius radiusSquaredA intersects the other + * circle with center (bX, bY) and square radius radiusSquaredB, and store the center of the line segment of + * intersection in the (x, y) components of the supplied vector and the half-length of that line segment in the z component. + *

+ * This method returns false when one circle contains the other circle. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param aX + * the x coordinate of the first circle's center + * @param aY + * the y coordinate of the first circle's center + * @param radiusSquaredA + * the square of the first circle's radius + * @param bX + * the x coordinate of the second circle's center + * @param bY + * the y coordinate of the second circle's center + * @param radiusSquaredB + * the square of the second circle's radius + * @param intersectionCenterAndHL + * will hold the center of the circle of intersection in the (x, y, z) components and the radius in the w component + * @return true iff both circles intersect; false otherwise + */ + public static boolean intersectCircleCircle(double aX, double aY, double radiusSquaredA, double bX, double bY, double radiusSquaredB, Vector3d intersectionCenterAndHL) { + double dX = bX - aX, dY = bY - aY; + double distSquared = dX * dX + dY * dY; + double h = 0.5 + (radiusSquaredA - radiusSquaredB) / distSquared; + double r_i = Math.sqrt(radiusSquaredA - h * h * distSquared); + if (r_i >= 0.0) { + intersectionCenterAndHL.x = aX + h * dX; + intersectionCenterAndHL.y = aY + h * dY; + intersectionCenterAndHL.z = r_i; + return true; + } + return false; + } + + /** + * Test whether the one circle with center centerA and square radius radiusSquaredA intersects the other + * circle with center centerB and square radius radiusSquaredB, and store the center of the line segment of + * intersection in the (x, y) components of the supplied vector and the half-length of that line segment in the z component. + *

+ * This method returns false when one circle contains the other circle. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param centerA + * the first circle's center + * @param radiusSquaredA + * the square of the first circle's radius + * @param centerB + * the second circle's center + * @param radiusSquaredB + * the square of the second circle's radius + * @param intersectionCenterAndHL + * will hold the center of the line segment of intersection in the (x, y) components and the half-length in the z component + * @return true iff both circles intersect; false otherwise + */ + public static boolean intersectCircleCircle(Vector2dc centerA, double radiusSquaredA, Vector2dc centerB, double radiusSquaredB, Vector3d intersectionCenterAndHL) { + return intersectCircleCircle(centerA.x(), centerA.y(), radiusSquaredA, centerB.x(), centerB.y(), radiusSquaredB, intersectionCenterAndHL); + } + + /** + * Test whether the one circle with center (aX, aY) and radius rA intersects the other circle with center (bX, bY) and radius rB. + *

+ * This method returns true when one circle contains the other circle. + *

+ * Reference: http://math.stackexchange.com/ + * + * @param aX + * the x coordinate of the first circle's center + * @param aY + * the y coordinate of the first circle's center + * @param rA + * the square of the first circle's radius + * @param bX + * the x coordinate of the second circle's center + * @param bY + * the y coordinate of the second circle's center + * @param rB + * the square of the second circle's radius + * @return true iff both circles intersect; false otherwise + */ + public static boolean testCircleCircle(double aX, double aY, double rA, double bX, double bY, double rB) { + double d = (aX - bX) * (aX - bX) + (aY - bY) * (aY - bY); + return d <= (rA + rB) * (rA + rB); + } + + /** + * Test whether the one circle with center centerA and square radius radiusSquaredA intersects the other + * circle with center centerB and square radius radiusSquaredB. + *

+ * This method returns true when one circle contains the other circle. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param centerA + * the first circle's center + * @param radiusSquaredA + * the square of the first circle's radius + * @param centerB + * the second circle's center + * @param radiusSquaredB + * the square of the second circle's radius + * @return true iff both circles intersect; false otherwise + */ + public static boolean testCircleCircle(Vector2dc centerA, double radiusSquaredA, Vector2dc centerB, double radiusSquaredB) { + return testCircleCircle(centerA.x(), centerA.y(), radiusSquaredA, centerB.x(), centerB.y(), radiusSquaredB); + } + + /** + * Determine the signed distance of the given point (pointX, pointY) to the line specified via its general plane equation + * a*x + b*y + c = 0. + *

+ * Reference: http://mathworld.wolfram.com + * + * @param pointX + * the x coordinate of the point + * @param pointY + * the y coordinate of the point + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the constant in the plane equation + * @return the distance between the point and the line + */ + public static double distancePointLine(double pointX, double pointY, double a, double b, double c) { + double denom = Math.sqrt(a * a + b * b); + return (a * pointX + b * pointY + c) / denom; + } + + /** + * Determine the signed distance of the given point (pointX, pointY) to the line defined by the two points (x0, y0) and (x1, y1). + *

+ * Reference: http://mathworld.wolfram.com + * + * @param pointX + * the x coordinate of the point + * @param pointY + * the y coordinate of the point + * @param x0 + * the x coordinate of the first point on the line + * @param y0 + * the y coordinate of the first point on the line + * @param x1 + * the x coordinate of the second point on the line + * @param y1 + * the y coordinate of the second point on the line + * @return the distance between the point and the line + */ + public static double distancePointLine(double pointX, double pointY, double x0, double y0, double x1, double y1) { + double dx = x1 - x0; + double dy = y1 - y0; + double denom = Math.sqrt(dx * dx + dy * dy); + return (dx * (y0 - pointY) - (x0 - pointX) * dy) / denom; + } + + /** + * Compute the distance of the given point (pX, pY, pZ) to the line defined by the two points (x0, y0, z0) and (x1, y1, z1). + *

+ * Reference: http://mathworld.wolfram.com + * + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the z coordinate of the point + * @param x0 + * the x coordinate of the first point on the line + * @param y0 + * the y coordinate of the first point on the line + * @param z0 + * the z coordinate of the first point on the line + * @param x1 + * the x coordinate of the second point on the line + * @param y1 + * the y coordinate of the second point on the line + * @param z1 + * the z coordinate of the second point on the line + * @return the distance between the point and the line + */ + public static double distancePointLine(double pX, double pY, double pZ, + double x0, double y0, double z0, double x1, double y1, double z1) { + double d21x = x1 - x0, d21y = y1 - y0, d21z = z1 - z0; + double d10x = x0 - pX, d10y = y0 - pY, d10z = z0 - pZ; + double cx = d21y * d10z - d21z * d10y, cy = d21z * d10x - d21x * d10z, cz = d21x * d10y - d21y * d10x; + return Math.sqrt((cx*cx + cy*cy + cz*cz) / (d21x*d21x + d21y*d21y + d21z*d21z)); + } + + /** + * Test whether the ray with given origin (originX, originY) and direction (dirX, dirY) intersects the line + * containing the given point (pointX, pointY) and having the normal (normalX, normalY), and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the line, because it is either parallel to the line or its direction points + * away from the line or the ray's origin is on the negative side of the line (i.e. the line's normal points away from the ray's origin). + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param pointX + * the x coordinate of a point on the line + * @param pointY + * the y coordinate of a point on the line + * @param normalX + * the x coordinate of the line's normal + * @param normalY + * the y coordinate of the line's normal + * @param epsilon + * some small epsilon for when the ray is parallel to the line + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the line; -1.0 otherwise + */ + public static double intersectRayLine(double originX, double originY, double dirX, double dirY, double pointX, double pointY, double normalX, double normalY, double epsilon) { + double denom = normalX * dirX + normalY * dirY; + if (denom < epsilon) { + double t = ((pointX - originX) * normalX + (pointY - originY) * normalY) / denom; + if (t >= 0.0) + return t; + } + return -1.0; + } + + /** + * Test whether the ray with given origin and direction dir intersects the line + * containing the given point and having the given normal, and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the line, because it is either parallel to the line or its direction points + * away from the line or the ray's origin is on the negative side of the line (i.e. the line's normal points away from the ray's origin). + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param point + * a point on the line + * @param normal + * the line's normal + * @param epsilon + * some small epsilon for when the ray is parallel to the line + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the line; -1.0 otherwise + */ + public static double intersectRayLine(Vector2dc origin, Vector2dc dir, Vector2dc point, Vector2dc normal, double epsilon) { + return intersectRayLine(origin.x(), origin.y(), dir.x(), dir.y(), point.x(), point.y(), normal.x(), normal.y(), epsilon); + } + + /** + * Determine whether the ray with given origin (originX, originY) and direction (dirX, dirY) intersects the undirected line segment + * given by the two end points (aX, bY) and (bX, bY), and return the value of the parameter t in the ray equation + * p(t) = origin + t * dir of the intersection point, if any. + *

+ * This method returns -1.0 if the ray does not intersect the line segment. + * + * @see #intersectRayLineSegment(Vector2dc, Vector2dc, Vector2dc, Vector2dc) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param aX + * the x coordinate of the line segment's first end point + * @param aY + * the y coordinate of the line segment's first end point + * @param bX + * the x coordinate of the line segment's second end point + * @param bY + * the y coordinate of the line segment's second end point + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the line segment; -1.0 otherwise + */ + public static double intersectRayLineSegment(double originX, double originY, double dirX, double dirY, double aX, double aY, double bX, double bY) { + double v1X = originX - aX; + double v1Y = originY - aY; + double v2X = bX - aX; + double v2Y = bY - aY; + double invV23 = 1.0 / (v2Y * dirX - v2X * dirY); + double t1 = (v2X * v1Y - v2Y * v1X) * invV23; + double t2 = (v1Y * dirX - v1X * dirY) * invV23; + if (t1 >= 0.0 && t2 >= 0.0 && t2 <= 1.0) + return t1; + return -1.0; + } + + /** + * Determine whether the ray with given origin and direction dir intersects the undirected line segment + * given by the two end points a and b, and return the value of the parameter t in the ray equation + * p(t) = origin + t * dir of the intersection point, if any. + *

+ * This method returns -1.0 if the ray does not intersect the line segment. + * + * @see #intersectRayLineSegment(double, double, double, double, double, double, double, double) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param a + * the line segment's first end point + * @param b + * the line segment's second end point + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the line segment; -1.0 otherwise + */ + public static double intersectRayLineSegment(Vector2dc origin, Vector2dc dir, Vector2dc a, Vector2dc b) { + return intersectRayLineSegment(origin.x(), origin.y(), dir.x(), dir.y(), a.x(), a.y(), b.x(), b.y()); + } + + /** + * Test whether the axis-aligned rectangle with minimum corner (minX, minY) and maximum corner (maxX, maxY) + * intersects the circle with the given center (centerX, centerY) and square radius radiusSquared. + *

+ * Reference: http://stackoverflow.com + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the square of the circle's radius + * @return true iff the axis-aligned rectangle intersects the circle; false otherwise + */ + public static boolean testAarCircle(double minX, double minY, double maxX, double maxY, double centerX, double centerY, double radiusSquared) { + double radius2 = radiusSquared; + if (centerX < minX) { + double d = (centerX - minX); + radius2 -= d * d; + } else if (centerX > maxX) { + double d = (centerX - maxX); + radius2 -= d * d; + } + if (centerY < minY) { + double d = (centerY - minY); + radius2 -= d * d; + } else if (centerY > maxY) { + double d = (centerY - maxY); + radius2 -= d * d; + } + return radius2 >= 0.0; + } + + /** + * Test whether the axis-aligned rectangle with minimum corner min and maximum corner max + * intersects the circle with the given center and square radius radiusSquared. + *

+ * Reference: http://stackoverflow.com + * + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @param center + * the circle's center + * @param radiusSquared + * the squared of the circle's radius + * @return true iff the axis-aligned rectangle intersects the circle; false otherwise + */ + public static boolean testAarCircle(Vector2dc min, Vector2dc max, Vector2dc center, double radiusSquared) { + return testAarCircle(min.x(), min.y(), max.x(), max.y(), center.x(), center.y(), radiusSquared); + } + + /** + * Determine the closest point on the triangle with the given vertices (v0X, v0Y), (v1X, v1Y), (v2X, v2Y) + * between that triangle and the given point (pX, pY) and store that point into the given result. + *

+ * Additionally, this method returns whether the closest point is a vertex ({@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}) + * of the triangle, lies on an edge ({@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20}) + * or on the {@link #POINT_ON_TRIANGLE_FACE face} of the triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.5 "Closest Point on Triangle to Point" + * + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param result + * will hold the closest point + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} + */ + public static int findClosestPointOnTriangle(double v0X, double v0Y, double v1X, double v1Y, double v2X, double v2Y, double pX, double pY, Vector2d result) { + double abX = v1X - v0X, abY = v1Y - v0Y; + double acX = v2X - v0X, acY = v2Y - v0Y; + double apX = pX - v0X, apY = pY - v0Y; + double d1 = abX * apX + abY * apY; + double d2 = acX * apX + acY * apY; + if (d1 <= 0.0 && d2 <= 0.0) { + result.x = v0X; + result.y = v0Y; + return POINT_ON_TRIANGLE_VERTEX_0; + } + double bpX = pX - v1X, bpY = pY - v1Y; + double d3 = abX * bpX + abY * bpY; + double d4 = acX * bpX + acY * bpY; + if (d3 >= 0.0 && d4 <= d3) { + result.x = v1X; + result.y = v1Y; + return POINT_ON_TRIANGLE_VERTEX_1; + } + double vc = d1 * d4 - d3 * d2; + if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) { + double v = d1 / (d1 - d3); + result.x = v0X + v * abX; + result.y = v0Y + v * abY; + return POINT_ON_TRIANGLE_EDGE_01; + } + double cpX = pX - v2X, cpY = pY - v2Y; + double d5 = abX * cpX + abY * cpY; + double d6 = acX * cpX + acY * cpY; + if (d6 >= 0.0 && d5 <= d6) { + result.x = v2X; + result.y = v2Y; + return POINT_ON_TRIANGLE_VERTEX_2; + } + double vb = d5 * d2 - d1 * d6; + if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) { + double w = d2 / (d2 - d6); + result.x = v0X + w * acX; + result.y = v0Y + w * acY; + return POINT_ON_TRIANGLE_EDGE_20; + } + double va = d3 * d6 - d5 * d4; + if (va <= 0.0 && d4 - d3 >= 0.0 && d5 - d6 >= 0.0) { + double w = (d4 - d3) / (d4 - d3 + d5 - d6); + result.x = v1X + w * (v2X - v1X); + result.y = v1Y + w * (v2Y - v1Y); + return POINT_ON_TRIANGLE_EDGE_12; + } + double denom = 1.0 / (va + vb + vc); + double v = vb * denom; + double w = vc * denom; + result.x = v0X + abX * v + acX * w; + result.y = v0Y + abY * v + acY * w; + return POINT_ON_TRIANGLE_FACE; + } + + /** + * Determine the closest point on the triangle with the vertices v0, v1, v2 + * between that triangle and the given point p and store that point into the given result. + *

+ * Additionally, this method returns whether the closest point is a vertex ({@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}) + * of the triangle, lies on an edge ({@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20}) + * or on the {@link #POINT_ON_TRIANGLE_FACE face} of the triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.5 "Closest Point on Triangle to Point" + * + * @param v0 + * the first vertex of the triangle + * @param v1 + * the second vertex of the triangle + * @param v2 + * the third vertex of the triangle + * @param p + * the point + * @param result + * will hold the closest point + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} + */ + public static int findClosestPointOnTriangle(Vector2dc v0, Vector2dc v1, Vector2dc v2, Vector2dc p, Vector2d result) { + return findClosestPointOnTriangle(v0.x(), v0.y(), v1.x(), v1.y(), v2.x(), v2.y(), p.x(), p.y(), result); + } + + /** + * Test whether the given ray with the origin (originX, originY) and direction (dirX, dirY) + * intersects the given circle with center (centerX, centerY) and square radius radiusSquared, + * and store the values of the parameter t in the ray equation p(t) = origin + t * dir for both points (near + * and far) of intersections into the given result vector. + *

+ * This method returns true for a ray whose origin lies inside the circle. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the circle radius squared + * @param result + * a vector that will contain the values of the parameter t in the ray equation + * p(t) = origin + t * dir for both points (near, far) of intersections with the circle + * @return true if the ray intersects the circle; false otherwise + */ + public static boolean intersectRayCircle(double originX, double originY, double dirX, double dirY, + double centerX, double centerY, double radiusSquared, Vector2d result) { + double Lx = centerX - originX; + double Ly = centerY - originY; + double tca = Lx * dirX + Ly * dirY; + double d2 = Lx * Lx + Ly * Ly - tca * tca; + if (d2 > radiusSquared) + return false; + double thc = Math.sqrt(radiusSquared - d2); + double t0 = tca - thc; + double t1 = tca + thc; + if (t0 < t1 && t1 >= 0.0) { + result.x = t0; + result.y = t1; + return true; + } + return false; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the circle with the given center and square radius radiusSquared, + * and store the values of the parameter t in the ray equation p(t) = origin + t * dir for both points (near + * and far) of intersections into the given result vector. + *

+ * This method returns true for a ray whose origin lies inside the circle. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param center + * the circle's center + * @param radiusSquared + * the circle radius squared + * @param result + * a vector that will contain the values of the parameter t in the ray equation + * p(t) = origin + t * dir for both points (near, far) of intersections with the circle + * @return true if the ray intersects the circle; false otherwise + */ + public static boolean intersectRayCircle(Vector2dc origin, Vector2dc dir, Vector2dc center, double radiusSquared, Vector2d result) { + return intersectRayCircle(origin.x(), origin.y(), dir.x(), dir.y(), center.x(), center.y(), radiusSquared, result); + } + + /** + * Test whether the given ray with the origin (originX, originY) and direction (dirX, dirY) + * intersects the given circle with center (centerX, centerY) and square radius radiusSquared. + *

+ * This method returns true for a ray whose origin lies inside the circle. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the circle radius squared + * @return true if the ray intersects the circle; false otherwise + */ + public static boolean testRayCircle(double originX, double originY, double dirX, double dirY, + double centerX, double centerY, double radiusSquared) { + double Lx = centerX - originX; + double Ly = centerY - originY; + double tca = Lx * dirX + Ly * dirY; + double d2 = Lx * Lx + Ly * Ly - tca * tca; + if (d2 > radiusSquared) + return false; + double thc = Math.sqrt(radiusSquared - d2); + double t0 = tca - thc; + double t1 = tca + thc; + return t0 < t1 && t1 >= 0.0; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the circle with the given center and square radius. + *

+ * This method returns true for a ray whose origin lies inside the circle. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param center + * the circle's center + * @param radiusSquared + * the circle radius squared + * @return true if the ray intersects the circle; false otherwise + */ + public static boolean testRayCircle(Vector2dc origin, Vector2dc dir, Vector2dc center, double radiusSquared) { + return testRayCircle(origin.x(), origin.y(), dir.x(), dir.y(), center.x(), center.y(), radiusSquared); + } + + /** + * Determine whether the given ray with the origin (originX, originY) and direction (dirX, dirY) + * intersects the axis-aligned rectangle given as its minimum corner (minX, minY) and maximum corner (maxX, maxY), + * and return the values of the parameter t in the ray equation p(t) = origin + t * dir of the near and far point of intersection + * as well as the side of the axis-aligned rectangle the ray intersects. + *

+ * This method also detects an intersection for a ray whose origin lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectRayAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param result + * a vector which will hold the values of the parameter t in the ray equation + * p(t) = origin + t * dir of the near and far point of intersection + * @return the side on which the near intersection occurred as one of + * {@link #AAR_SIDE_MINX}, {@link #AAR_SIDE_MINY}, {@link #AAR_SIDE_MAXX} or {@link #AAR_SIDE_MAXY}; + * or -1 if the ray does not intersect the axis-aligned rectangle; + */ + public static int intersectRayAar(double originX, double originY, double dirX, double dirY, + double minX, double minY, double maxX, double maxY, Vector2d result) { + double invDirX = 1.0 / dirX, invDirY = 1.0 / dirY; + double tNear, tFar, tymin, tymax; + if (invDirX >= 0.0) { + tNear = (minX - originX) * invDirX; + tFar = (maxX - originX) * invDirX; + } else { + tNear = (maxX - originX) * invDirX; + tFar = (minX - originX) * invDirX; + } + if (invDirY >= 0.0) { + tymin = (minY - originY) * invDirY; + tymax = (maxY - originY) * invDirY; + } else { + tymin = (maxY - originY) * invDirY; + tymax = (minY - originY) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return OUTSIDE; + tNear = tymin > tNear || Double.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Double.isNaN(tFar) ? tymax : tFar; + int side = -1; // no intersection side + if (tNear < tFar && tFar >= 0.0) { + double px = originX + tNear * dirX; + double py = originY + tNear * dirY; + result.x = tNear; + result.y = tFar; + double daX = Math.abs(px - minX); + double daY = Math.abs(py - minY); + double dbX = Math.abs(px - maxX); + double dbY = Math.abs(py - maxY); + side = 0; // min x coordinate + double min = daX; + if (daY < min) { + min = daY; + side = 1; // min y coordinate + } + if (dbX < min) { + min = dbX; + side = 2; // max xcoordinate + } + if (dbY < min) + side = 3; // max y coordinate + } + return side; + } + + /** + * Determine whether the given ray with the given origin and direction dir + * intersects the axis-aligned rectangle given as its minimum corner min and maximum corner max, + * and return the values of the parameter t in the ray equation p(t) = origin + t * dir of the near and far point of intersection + * as well as the side of the axis-aligned rectangle the ray intersects. + *

+ * This method also detects an intersection for a ray whose origin lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectRayAar(double, double, double, double, double, double, double, double, Vector2d) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @param result + * a vector which will hold the values of the parameter t in the ray equation + * p(t) = origin + t * dir of the near and far point of intersection + * @return the side on which the near intersection occurred as one of + * {@link #AAR_SIDE_MINX}, {@link #AAR_SIDE_MINY}, {@link #AAR_SIDE_MAXX} or {@link #AAR_SIDE_MAXY}; + * or -1 if the ray does not intersect the axis-aligned rectangle; + */ + public static int intersectRayAar(Vector2dc origin, Vector2dc dir, Vector2dc min, Vector2dc max, Vector2d result) { + return intersectRayAar(origin.x(), origin.y(), dir.x(), dir.y(), min.x(), min.y(), max.x(), max.y(), result); + } + + /** + * Determine whether the undirected line segment with the end points (p0X, p0Y) and (p1X, p1Y) + * intersects the axis-aligned rectangle given as its minimum corner (minX, minY) and maximum corner (maxX, maxY), + * and store the values of the parameter t in the ray equation p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * into result. + *

+ * This method also detects an intersection of a line segment whose either end point lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectLineSegmentAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc, Vector2d) + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param result + * a vector which will hold the values of the parameter t in the ray equation + * p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * @return {@link #INSIDE} if the line segment lies completely inside of the axis-aligned rectangle; or + * {@link #OUTSIDE} if the line segment lies completely outside of the axis-aligned rectangle; or + * {@link #ONE_INTERSECTION} if one of the end points of the line segment lies inside of the axis-aligned rectangle; or + * {@link #TWO_INTERSECTION} if the line segment intersects two edges of the axis-aligned rectangle or lies on one edge of the rectangle + */ + public static int intersectLineSegmentAar(double p0X, double p0Y, double p1X, double p1Y, + double minX, double minY, double maxX, double maxY, Vector2d result) { + double dirX = p1X - p0X, dirY = p1Y - p0Y; + double invDirX = 1.0 / dirX, invDirY = 1.0 / dirY; + double tNear, tFar, tymin, tymax; + if (invDirX >= 0.0) { + tNear = (minX - p0X) * invDirX; + tFar = (maxX - p0X) * invDirX; + } else { + tNear = (maxX - p0X) * invDirX; + tFar = (minX - p0X) * invDirX; + } + if (invDirY >= 0.0) { + tymin = (minY - p0Y) * invDirY; + tymax = (maxY - p0Y) * invDirY; + } else { + tymin = (maxY - p0Y) * invDirY; + tymax = (minY - p0Y) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return OUTSIDE; + tNear = tymin > tNear || Double.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Double.isNaN(tFar) ? tymax : tFar; + int type = OUTSIDE; + if (tNear <= tFar && tNear <= 1.0f && tFar >= 0.0f) { + if (tNear >= 0.0f && tFar > 1.0f) { + tFar = tNear; + type = ONE_INTERSECTION; + } else if (tNear < 0.0f && tFar <= 1.0f) { + tNear = tFar; + type = ONE_INTERSECTION; + } else if (tNear < 0.0f && tFar > 1.0f) { + type = INSIDE; + } else { + type = TWO_INTERSECTION; + } + result.x = tNear; + result.y = tFar; + } + return type; + } + + /** + * Determine whether the undirected line segment with the end points p0 and p1 + * intersects the axis-aligned rectangle given as its minimum corner min and maximum corner max, + * and store the values of the parameter t in the ray equation p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * into result. + *

+ * This method also detects an intersection of a line segment whose either end point lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * #see {@link #intersectLineSegmentAar(double, double, double, double, double, double, double, double, Vector2d)} + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @param result + * a vector which will hold the values of the parameter t in the ray equation + * p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * @return {@link #INSIDE} if the line segment lies completely inside of the axis-aligned rectangle; or + * {@link #OUTSIDE} if the line segment lies completely outside of the axis-aligned rectangle; or + * {@link #ONE_INTERSECTION} if one of the end points of the line segment lies inside of the axis-aligned rectangle; or + * {@link #TWO_INTERSECTION} if the line segment intersects two edges of the axis-aligned rectangle + */ + public static int intersectLineSegmentAar(Vector2dc p0, Vector2dc p1, Vector2dc min, Vector2dc max, Vector2d result) { + return intersectLineSegmentAar(p0.x(), p0.y(), p1.x(), p1.y(), min.x(), min.y(), max.x(), max.y(), result); + } + + /** + * Test whether the given ray with the origin (originX, originY) and direction (dirX, dirY) + * intersects the given axis-aligned rectangle given as its minimum corner (minX, minY) and maximum corner (maxX, maxY). + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #testRayAar(Vector2dc, Vector2dc, Vector2dc, Vector2dc) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @return true if the given ray intersects the axis-aligned rectangle; false otherwise + */ + public static boolean testRayAar(double originX, double originY, double dirX, double dirY, double minX, double minY, double maxX, double maxY) { + double invDirX = 1.0 / dirX, invDirY = 1.0 / dirY; + double tNear, tFar, tymin, tymax; + if (invDirX >= 0.0) { + tNear = (minX - originX) * invDirX; + tFar = (maxX - originX) * invDirX; + } else { + tNear = (maxX - originX) * invDirX; + tFar = (minX - originX) * invDirX; + } + if (invDirY >= 0.0) { + tymin = (minY - originY) * invDirY; + tymax = (maxY - originY) * invDirY; + } else { + tymin = (maxY - originY) * invDirY; + tymax = (minY - originY) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return false; + tNear = tymin > tNear || Double.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Double.isNaN(tFar) ? tymax : tFar; + return tNear < tFar && tFar >= 0.0; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the given axis-aligned rectangle specified as its minimum corner min and maximum corner max. + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #testRayAar(double, double, double, double, double, double, double, double) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @return true if the given ray intersects the axis-aligned rectangle; false otherwise + */ + public static boolean testRayAar(Vector2dc origin, Vector2dc dir, Vector2dc min, Vector2dc max) { + return testRayAar(origin.x(), origin.y(), dir.x(), dir.y(), min.x(), min.y(), max.x(), max.y()); + } + + /** + * Test whether the given point (pX, pY) lies inside the triangle with the vertices (v0X, v0Y), (v1X, v1Y), (v2X, v2Y). + * + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @return true iff the point lies inside the triangle; false otherwise + */ + public static boolean testPointTriangle(double pX, double pY, double v0X, double v0Y, double v1X, double v1Y, double v2X, double v2Y) { + boolean b1 = (pX - v1X) * (v0Y - v1Y) - (v0X - v1X) * (pY - v1Y) < 0.0; + boolean b2 = (pX - v2X) * (v1Y - v2Y) - (v1X - v2X) * (pY - v2Y) < 0.0; + if (b1 != b2) + return false; + boolean b3 = (pX - v0X) * (v2Y - v0Y) - (v2X - v0X) * (pY - v0Y) < 0.0; + return b2 == b3; + } + + /** + * Test whether the given point lies inside the triangle with the vertices v0, v1, v2. + * + * @param v0 + * the first vertex of the triangle + * @param v1 + * the second vertex of the triangle + * @param v2 + * the third vertex of the triangle + * @param point + * the point + * @return true iff the point lies inside the triangle; false otherwise + */ + public static boolean testPointTriangle(Vector2dc point, Vector2dc v0, Vector2dc v1, Vector2dc v2) { + return testPointTriangle(point.x(), point.y(), v0.x(), v0.y(), v1.x(), v1.y(), v2.x(), v2.y()); + } + + /** + * Test whether the given point (pX, pY) lies inside the axis-aligned rectangle with the minimum corner (minX, minY) + * and maximum corner (maxX, maxY). + * + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @return true iff the point lies inside the axis-aligned rectangle; false otherwise + */ + public static boolean testPointAar(double pX, double pY, double minX, double minY, double maxX, double maxY) { + return pX >= minX && pY >= minY && pX <= maxX && pY <= maxY; + } + + /** + * Test whether the point (pX, pY) lies inside the circle with center (centerX, centerY) and square radius radiusSquared. + * + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the square radius of the circle + * @return true iff the point lies inside the circle; false otherwise + */ + public static boolean testPointCircle(double pX, double pY, double centerX, double centerY, double radiusSquared) { + double dx = pX - centerX; + double dy = pY - centerY; + double dx2 = dx * dx; + double dy2 = dy * dy; + return dx2 + dy2 <= radiusSquared; + } + + /** + * Test whether the circle with center (centerX, centerY) and square radius radiusSquared intersects the triangle with counter-clockwise vertices + * (v0X, v0Y), (v1X, v1Y), (v2X, v2Y). + *

+ * The vertices of the triangle must be specified in counter-clockwise order. + *

+ * Reference: http://www.phatcode.net/ + * + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the square radius of the circle + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @return true iff the circle intersects the triangle; false otherwise + */ + public static boolean testCircleTriangle(double centerX, double centerY, double radiusSquared, double v0X, double v0Y, double v1X, double v1Y, double v2X, double v2Y) { + double c1x = centerX - v0X, c1y = centerY - v0Y; + double c1sqr = c1x * c1x + c1y * c1y - radiusSquared; + if (c1sqr <= 0.0) + return true; + double c2x = centerX - v1X, c2y = centerY - v1Y; + double c2sqr = c2x * c2x + c2y * c2y - radiusSquared; + if (c2sqr <= 0.0) + return true; + double c3x = centerX - v2X, c3y = centerY - v2Y; + double c3sqr = c3x * c3x + c3y * c3y - radiusSquared; + if (c3sqr <= 0.0) + return true; + double e1x = v1X - v0X, e1y = v1Y - v0Y; + double e2x = v2X - v1X, e2y = v2Y - v1Y; + double e3x = v0X - v2X, e3y = v0Y - v2Y; + if (e1x * c1y - e1y * c1x >= 0.0 && e2x * c2y - e2y * c2x >= 0.0 && e3x * c3y - e3y * c3x >= 0.0) + return true; + double k = c1x * e1x + c1y * e1y; + if (k >= 0.0) { + double len = e1x * e1x + e1y * e1y; + if (k <= len) { + if (c1sqr * len <= k * k) + return true; + } + } + k = c2x * e2x + c2y * e2y; + if (k > 0.0) { + double len = e2x * e2x + e2y * e2y; + if (k <= len) { + if (c2sqr * len <= k * k) + return true; + } + } + k = c3x * e3x + c3y * e3y; + if (k >= 0.0) { + double len = e3x * e3x + e3y * e3y; + if (k < len) { + if (c3sqr * len <= k * k) + return true; + } + } + return false; + } + + /** + * Test whether the circle with given center and square radius radiusSquared intersects the triangle with counter-clockwise vertices + * v0, v1, v2. + *

+ * The vertices of the triangle must be specified in counter-clockwise order. + *

+ * Reference: http://www.phatcode.net/ + * + * @param center + * the circle's center + * @param radiusSquared + * the square radius of the circle + * @param v0 + * the first vertex of the triangle + * @param v1 + * the second vertex of the triangle + * @param v2 + * the third vertex of the triangle + * @return true iff the circle intersects the triangle; false otherwise + */ + public static boolean testCircleTriangle(Vector2dc center, double radiusSquared, Vector2dc v0, Vector2dc v1, Vector2dc v2) { + return testCircleTriangle(center.x(), center.y(), radiusSquared, v0.x(), v0.y(), v1.x(), v1.y(), v2.x(), v2.y()); + } + + /** + * Determine whether the polygon specified by the given sequence of (x, y) coordinate pairs intersects with the ray + * with given origin (originX, originY, originZ) and direction (dirX, dirY, dirZ), and store the point of intersection + * into the given vector p. + *

+ * If the polygon intersects the ray, this method returns the index of the polygon edge intersecting the ray, that is, the index of the + * first vertex of the directed line segment. The second vertex is always that index + 1, modulus the number of polygon vertices. + * + * @param verticesXY + * the sequence of (x, y) coordinate pairs of all vertices of the polygon + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param p + * will hold the point of intersection + * @return the index of the first vertex of the polygon edge that intersects the ray; or -1 if the ray does not intersect the polygon + */ + public static int intersectPolygonRay(double[] verticesXY, double originX, double originY, double dirX, double dirY, Vector2d p) { + double nearestT = Double.POSITIVE_INFINITY; + int count = verticesXY.length >> 1; + int edgeIndex = -1; + double aX = verticesXY[(count-1)<<1], aY = verticesXY[((count-1)<<1) + 1]; + for (int i = 0; i < count; i++) { + double bX = verticesXY[i << 1], bY = verticesXY[(i << 1) + 1]; + double doaX = originX - aX, doaY = originY - aY; + double dbaX = bX - aX, dbaY = bY - aY; + double invDbaDir = 1.0 / (dbaY * dirX - dbaX * dirY); + double t = (dbaX * doaY - dbaY * doaX) * invDbaDir; + if (t >= 0.0 && t < nearestT) { + double t2 = (doaY * dirX - doaX * dirY) * invDbaDir; + if (t2 >= 0.0 && t2 <= 1.0) { + edgeIndex = (i - 1 + count) % count; + nearestT = t; + p.x = originX + t * dirX; + p.y = originY + t * dirY; + } + } + aX = bX; + aY = bY; + } + return edgeIndex; + } + + /** + * Determine whether the polygon specified by the given sequence of vertices intersects with the ray + * with given origin (originX, originY, originZ) and direction (dirX, dirY, dirZ), and store the point of intersection + * into the given vector p. + *

+ * If the polygon intersects the ray, this method returns the index of the polygon edge intersecting the ray, that is, the index of the + * first vertex of the directed line segment. The second vertex is always that index + 1, modulus the number of polygon vertices. + * + * @param vertices + * the sequence of (x, y) coordinate pairs of all vertices of the polygon + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param p + * will hold the point of intersection + * @return the index of the first vertex of the polygon edge that intersects the ray; or -1 if the ray does not intersect the polygon + */ + public static int intersectPolygonRay(Vector2dc[] vertices, double originX, double originY, double dirX, double dirY, Vector2d p) { + double nearestT = Double.POSITIVE_INFINITY; + int count = vertices.length; + int edgeIndex = -1; + double aX = vertices[count-1].x(), aY = vertices[count-1].y(); + for (int i = 0; i < count; i++) { + Vector2dc b = vertices[i]; + double bX = b.x(), bY = b.y(); + double doaX = originX - aX, doaY = originY - aY; + double dbaX = bX - aX, dbaY = bY - aY; + double invDbaDir = 1.0 / (dbaY * dirX - dbaX * dirY); + double t = (dbaX * doaY - dbaY * doaX) * invDbaDir; + if (t >= 0.0 && t < nearestT) { + double t2 = (doaY * dirX - doaX * dirY) * invDbaDir; + if (t2 >= 0.0 && t2 <= 1.0) { + edgeIndex = (i - 1 + count) % count; + nearestT = t; + p.x = originX + t * dirX; + p.y = originY + t * dirY; + } + } + aX = bX; + aY = bY; + } + return edgeIndex; + } + + /** + * Determine whether the two lines, specified via two points lying on each line, intersect each other, and store the point of intersection + * into the given vector p. + * + * @param ps1x + * the x coordinate of the first point on the first line + * @param ps1y + * the y coordinate of the first point on the first line + * @param pe1x + * the x coordinate of the second point on the first line + * @param pe1y + * the y coordinate of the second point on the first line + * @param ps2x + * the x coordinate of the first point on the second line + * @param ps2y + * the y coordinate of the first point on the second line + * @param pe2x + * the x coordinate of the second point on the second line + * @param pe2y + * the y coordinate of the second point on the second line + * @param p + * will hold the point of intersection + * @return true iff the two lines intersect; false otherwise + */ + public static boolean intersectLineLine(double ps1x, double ps1y, double pe1x, double pe1y, double ps2x, double ps2y, double pe2x, double pe2y, Vector2d p) { + double d1x = ps1x - pe1x; + double d1y = pe1y - ps1y; + double d1ps1 = d1y * ps1x + d1x * ps1y; + double d2x = ps2x - pe2x; + double d2y = pe2y - ps2y; + double d2ps2 = d2y * ps2x + d2x * ps2y; + double det = d1y * d2x - d2y * d1x; + if (det == 0.0) + return false; + p.x = (d2x * d1ps1 - d1x * d2ps2) / det; + p.y = (d1y * d2ps2 - d2y * d1ps1) / det; + return true; + } + + private static boolean separatingAxis(Vector2d[] v1s, Vector2d[] v2s, double aX, double aY) { + double minA = Double.POSITIVE_INFINITY, maxA = Double.NEGATIVE_INFINITY; + double minB = Double.POSITIVE_INFINITY, maxB = Double.NEGATIVE_INFINITY; + int maxLen = Math.max(v1s.length, v2s.length); + /* Project both polygons on axis */ + for (int k = 0; k < maxLen; k++) { + if (k < v1s.length) { + Vector2d v1 = v1s[k]; + double d = v1.x * aX + v1.y * aY; + if (d < minA) minA = d; + if (d > maxA) maxA = d; + } + if (k < v2s.length) { + Vector2d v2 = v2s[k]; + double d = v2.x * aX + v2.y * aY; + if (d < minB) minB = d; + if (d > maxB) maxB = d; + } + /* Early-out if overlap found */ + if (minA <= maxB && minB <= maxA) { + return false; + } + } + return true; + } + + /** + * Test if the two convex polygons, given via their vertices, intersect. + * + * @param v1s + * the vertices of the first convex polygon + * @param v2s + * the vertices of the second convex polygon + * @return true if the convex polygons intersect; false otherwise + */ + public static boolean testPolygonPolygon(Vector2d[] v1s, Vector2d[] v2s) { + /* Try to find a separating axis using the first polygon's edges */ + for (int i = 0, j = v1s.length - 1; i < v1s.length; j = i, i++) { + Vector2d s = v1s[i], t = v1s[j]; + if (separatingAxis(v1s, v2s, s.y - t.y, t.x - s.x)) + return false; + } + /* Try to find a separating axis using the second polygon's edges */ + for (int i = 0, j = v2s.length - 1; i < v2s.length; j = i, i++) { + Vector2d s = v2s[i], t = v2s[j]; + if (separatingAxis(v1s, v2s, s.y - t.y, t.x - s.x)) + return false; + } + return true; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Intersectionf.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Intersectionf.java new file mode 100644 index 000000000..fac99d454 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Intersectionf.java @@ -0,0 +1,4789 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Contains intersection and distance tests for some 2D and 3D geometric primitives. + * + * @author Kai Burjack + */ +public class Intersectionf { + + /** + * Return value of + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, float, float, float, float, Vector3f)}, + * {@link #findClosestPointOnTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3f)}, + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #findClosestPointOnTriangle(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point is the first vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_VERTEX_0 = 1; + /** + * Return value of + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, float, float, float, float, Vector3f)}, + * {@link #findClosestPointOnTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3f)}, + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #findClosestPointOnTriangle(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point is the second vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_VERTEX_1 = 2; + /** + * Return value of + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, float, float, float, float, Vector3f)}, + * {@link #findClosestPointOnTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3f)}, + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #findClosestPointOnTriangle(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point is the third vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_VERTEX_2 = 3; + + /** + * Return value of + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, float, float, float, float, Vector3f)}, + * {@link #findClosestPointOnTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3f)}, + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #findClosestPointOnTriangle(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point lies on the edge between the first and second vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_EDGE_01 = 4; + /** + * Return value of + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, float, float, float, float, Vector3f)}, + * {@link #findClosestPointOnTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3f)}, + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #findClosestPointOnTriangle(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point lies on the edge between the second and third vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_EDGE_12 = 5; + /** + * Return value of + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, float, float, float, float, Vector3f)}, + * {@link #findClosestPointOnTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3f)}, + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #findClosestPointOnTriangle(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point lies on the edge between the third and first vertex of the triangle. + */ + public static final int POINT_ON_TRIANGLE_EDGE_20 = 6; + + /** + * Return value of + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, float, float, float, float, Vector3f)}, + * {@link #findClosestPointOnTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3f)}, + * {@link #findClosestPointOnTriangle(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #findClosestPointOnTriangle(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} or + * {@link #intersectSweptSphereTriangle} + * to signal that the closest point lies on the face of the triangle. + */ + public static final int POINT_ON_TRIANGLE_FACE = 7; + + /** + * Return value of {@link #intersectRayAar(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectRayAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} + * to indicate that the ray intersects the side of the axis-aligned rectangle with the minimum x coordinate. + */ + public static final int AAR_SIDE_MINX = 0; + /** + * Return value of {@link #intersectRayAar(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectRayAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} + * to indicate that the ray intersects the side of the axis-aligned rectangle with the minimum y coordinate. + */ + public static final int AAR_SIDE_MINY = 1; + /** + * Return value of {@link #intersectRayAar(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectRayAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} + * to indicate that the ray intersects the side of the axis-aligned rectangle with the maximum x coordinate. + */ + public static final int AAR_SIDE_MAXX = 2; + /** + * Return value of {@link #intersectRayAar(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectRayAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} + * to indicate that the ray intersects the side of the axis-aligned rectangle with the maximum y coordinate. + */ + public static final int AAR_SIDE_MAXY = 3; + + /** + * Return value of {@link #intersectLineSegmentAab(float, float, float, float, float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectLineSegmentAab(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector2f)} to indicate that the line segment does not intersect the axis-aligned box; + * or return value of {@link #intersectLineSegmentAar(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectLineSegmentAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} to indicate that the line segment does not intersect the axis-aligned rectangle. + */ + public static final int OUTSIDE = -1; + /** + * Return value of {@link #intersectLineSegmentAab(float, float, float, float, float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectLineSegmentAab(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector2f)} to indicate that one end point of the line segment lies inside of the axis-aligned box; + * or return value of {@link #intersectLineSegmentAar(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectLineSegmentAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} to indicate that one end point of the line segment lies inside of the axis-aligned rectangle. + */ + public static final int ONE_INTERSECTION = 1; + /** + * Return value of {@link #intersectLineSegmentAab(float, float, float, float, float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectLineSegmentAab(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector2f)} to indicate that the line segment intersects two sides of the axis-aligned box + * or lies on an edge or a side of the box; + * or return value of {@link #intersectLineSegmentAar(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectLineSegmentAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} to indicate that the line segment intersects two edges of the axis-aligned rectangle + * or lies on an edge of the rectangle. + */ + public static final int TWO_INTERSECTION = 2; + /** + * Return value of {@link #intersectLineSegmentAab(float, float, float, float, float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectLineSegmentAab(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector2f)} to indicate that the line segment lies completely inside of the axis-aligned box; + * or return value of {@link #intersectLineSegmentAar(float, float, float, float, float, float, float, float, Vector2f)} and + * {@link #intersectLineSegmentAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f)} to indicate that the line segment lies completely inside of the axis-aligned rectangle. + */ + public static final int INSIDE = 3; + + /** + * Test whether the plane with the general plane equation a*x + b*y + c*z + d = 0 intersects the sphere with center + * (centerX, centerY, centerZ) and radius. + *

+ * Reference: http://math.stackexchange.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radius + * the radius of the sphere + * @return true iff the plane intersects the sphere; false otherwise + */ + public static boolean testPlaneSphere( + float a, float b, float c, float d, + float centerX, float centerY, float centerZ, float radius) { + float denom = (float) Math.sqrt(a * a + b * b + c * c); + float dist = (a * centerX + b * centerY + c * centerZ + d) / denom; + return -radius <= dist && dist <= radius; + } + + /** + * Test whether the plane with the general plane equation a*x + b*y + c*z + d = 0 intersects the sphere with center + * (centerX, centerY, centerZ) and radius, and store the center of the circle of + * intersection in the (x, y, z) components of the supplied vector and the radius of that circle in the w component. + *

+ * Reference: http://math.stackexchange.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radius + * the radius of the sphere + * @param intersectionCenterAndRadius + * will hold the center of the circle of intersection in the (x, y, z) components and the radius in the w component + * @return true iff the plane intersects the sphere; false otherwise + */ + public static boolean intersectPlaneSphere( + float a, float b, float c, float d, + float centerX, float centerY, float centerZ, float radius, + Vector4f intersectionCenterAndRadius) { + float invDenom = Math.invsqrt(a * a + b * b + c * c); + float dist = (a * centerX + b * centerY + c * centerZ + d) * invDenom; + if (-radius <= dist && dist <= radius) { + intersectionCenterAndRadius.x = centerX + dist * a * invDenom; + intersectionCenterAndRadius.y = centerY + dist * b * invDenom; + intersectionCenterAndRadius.z = centerZ + dist * c * invDenom; + intersectionCenterAndRadius.w = (float) Math.sqrt(radius * radius - dist * dist); + return true; + } + return false; + } + + /** + * Test whether the plane with the general plane equation a*x + b*y + c*z + d = 0 intersects the moving sphere with center + * (cX, cY, cZ), radius and velocity (vX, vY, vZ), and store the point of intersection + * in the (x, y, z) components of the supplied vector and the time of intersection in the w component. + *

+ * The normal vector (a, b, c) of the plane equation needs to be normalized. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.5.3 "Intersecting Moving Sphere Against Plane" + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param cX + * the x coordinate of the center position of the sphere at t=0 + * @param cY + * the y coordinate of the center position of the sphere at t=0 + * @param cZ + * the z coordinate of the center position of the sphere at t=0 + * @param radius + * the sphere's radius + * @param vX + * the x component of the velocity of the sphere + * @param vY + * the y component of the velocity of the sphere + * @param vZ + * the z component of the velocity of the sphere + * @param pointAndTime + * will hold the point and time of intersection (if any) + * @return true iff the sphere intersects the plane; false otherwise + */ + public static boolean intersectPlaneSweptSphere( + float a, float b, float c, float d, + float cX, float cY, float cZ, float radius, + float vX, float vY, float vZ, + Vector4f pointAndTime) { + // Compute distance of sphere center to plane + float dist = a * cX + b * cY + c * cZ - d; + if (Math.abs(dist) <= radius) { + // The sphere is already overlapping the plane. Set time of + // intersection to zero and q to sphere center + pointAndTime.set(cX, cY, cZ, 0.0f); + return true; + } + float denom = a * vX + b * vY + c * vZ; + if (denom * dist >= 0.0f) { + // No intersection as sphere moving parallel to or away from plane + return false; + } + // Sphere is moving towards the plane + // Use +r in computations if sphere in front of plane, else -r + float r = dist > 0.0f ? radius : -radius; + float t = (r - dist) / denom; + pointAndTime.set( + cX + t * vX - r * a, + cY + t * vY - r * b, + cZ + t * vZ - r * c, + t); + return true; + } + + /** + * Test whether the plane with the general plane equation a*x + b*y + c*z + d = 0 intersects the sphere moving from center + * position (t0X, t0Y, t0Z) to (t1X, t1Y, t1Z) and having the given radius. + *

+ * The normal vector (a, b, c) of the plane equation needs to be normalized. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.5.3 "Intersecting Moving Sphere Against Plane" + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param t0X + * the x coordinate of the start position of the sphere + * @param t0Y + * the y coordinate of the start position of the sphere + * @param t0Z + * the z coordinate of the start position of the sphere + * @param r + * the sphere's radius + * @param t1X + * the x coordinate of the end position of the sphere + * @param t1Y + * the y coordinate of the end position of the sphere + * @param t1Z + * the z coordinate of the end position of the sphere + * @return true if the sphere intersects the plane; false otherwise + */ + public static boolean testPlaneSweptSphere( + float a, float b, float c, float d, + float t0X, float t0Y, float t0Z, float r, + float t1X, float t1Y, float t1Z) { + // Get the distance for both a and b from plane p + float adist = t0X * a + t0Y * b + t0Z * c - d; + float bdist = t1X * a + t1Y * b + t1Z * c - d; + // Intersects if on different sides of plane (distances have different signs) + if (adist * bdist < 0.0f) return true; + // Intersects if start or end position within radius from plane + if (Math.abs(adist) <= r || Math.abs(bdist) <= r) return true; + // No intersection + return false; + } + + /** + * Test whether the axis-aligned box with minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ) + * intersects the plane with the general equation a*x + b*y + c*z + d = 0. + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the z coordinate of the maximum corner of the axis-aligned box + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return true iff the axis-aligned box intersects the plane; false otherwise + */ + public static boolean testAabPlane( + float minX, float minY, float minZ, + float maxX, float maxY, float maxZ, + float a, float b, float c, float d) { + float pX, pY, pZ, nX, nY, nZ; + if (a > 0.0f) { + pX = maxX; + nX = minX; + } else { + pX = minX; + nX = maxX; + } + if (b > 0.0f) { + pY = maxY; + nY = minY; + } else { + pY = minY; + nY = maxY; + } + if (c > 0.0f) { + pZ = maxZ; + nZ = minZ; + } else { + pZ = minZ; + nZ = maxZ; + } + float distN = d + a * nX + b * nY + c * nZ; + float distP = d + a * pX + b * pY + c * pZ; + return distN <= 0.0f && distP >= 0.0f; + } + + /** + * Test whether the axis-aligned box with minimum corner min and maximum corner max + * intersects the plane with the general equation a*x + b*y + c*z + d = 0. + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return true iff the axis-aligned box intersects the plane; false otherwise + */ + public static boolean testAabPlane(Vector3fc min, Vector3fc max, float a, float b, float c, float d) { + return testAabPlane(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), a, b, c, d); + } + + /** + * Test whether the axis-aligned box with minimum corner (minXA, minYA, minZA) and maximum corner (maxXA, maxYA, maxZA) + * intersects the axis-aligned box with minimum corner (minXB, minYB, minZB) and maximum corner (maxXB, maxYB, maxZB). + * + * @param minXA + * the x coordinate of the minimum corner of the first axis-aligned box + * @param minYA + * the y coordinate of the minimum corner of the first axis-aligned box + * @param minZA + * the z coordinate of the minimum corner of the first axis-aligned box + * @param maxXA + * the x coordinate of the maximum corner of the first axis-aligned box + * @param maxYA + * the y coordinate of the maximum corner of the first axis-aligned box + * @param maxZA + * the z coordinate of the maximum corner of the first axis-aligned box + * @param minXB + * the x coordinate of the minimum corner of the second axis-aligned box + * @param minYB + * the y coordinate of the minimum corner of the second axis-aligned box + * @param minZB + * the z coordinate of the minimum corner of the second axis-aligned box + * @param maxXB + * the x coordinate of the maximum corner of the second axis-aligned box + * @param maxYB + * the y coordinate of the maximum corner of the second axis-aligned box + * @param maxZB + * the z coordinate of the maximum corner of the second axis-aligned box + * @return true iff both axis-aligned boxes intersect; false otherwise + */ + public static boolean testAabAab( + float minXA, float minYA, float minZA, + float maxXA, float maxYA, float maxZA, + float minXB, float minYB, float minZB, + float maxXB, float maxYB, float maxZB) { + return maxXA >= minXB && maxYA >= minYB && maxZA >= minZB && + minXA <= maxXB && minYA <= maxYB && minZA <= maxZB; + } + + /** + * Test whether the axis-aligned box with minimum corner minA and maximum corner maxA + * intersects the axis-aligned box with minimum corner minB and maximum corner maxB. + * + * @param minA + * the minimum corner of the first axis-aligned box + * @param maxA + * the maximum corner of the first axis-aligned box + * @param minB + * the minimum corner of the second axis-aligned box + * @param maxB + * the maximum corner of the second axis-aligned box + * @return true iff both axis-aligned boxes intersect; false otherwise + */ + public static boolean testAabAab(Vector3fc minA, Vector3fc maxA, Vector3fc minB, Vector3fc maxB) { + return testAabAab(minA.x(), minA.y(), minA.z(), maxA.x(), maxA.y(), maxA.z(), minB.x(), minB.y(), minB.z(), maxB.x(), maxB.y(), maxB.z()); + } + + /** + * Test whether two oriented boxes given via their center position, orientation and half-size, intersect. + *

+ * The orientation of a box is given as three unit vectors spanning the local orthonormal basis of the box. + *

+ * The size is given as the half-size along each of the unit vectors defining the orthonormal basis. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 4.4.1 "OBB-OBB Intersection" + * + * @param b0c + * the center of the first box + * @param b0uX + * the local X unit vector of the first box + * @param b0uY + * the local Y unit vector of the first box + * @param b0uZ + * the local Z unit vector of the first box + * @param b0hs + * the half-size of the first box + * @param b1c + * the center of the second box + * @param b1uX + * the local X unit vector of the second box + * @param b1uY + * the local Y unit vector of the second box + * @param b1uZ + * the local Z unit vector of the second box + * @param b1hs + * the half-size of the second box + * @return true if both boxes intersect; false otherwise + */ + public static boolean testObOb( + Vector3f b0c, Vector3f b0uX, Vector3f b0uY, Vector3f b0uZ, Vector3f b0hs, + Vector3f b1c, Vector3f b1uX, Vector3f b1uY, Vector3f b1uZ, Vector3f b1hs) { + return testObOb( + b0c.x, b0c.y, b0c.z, b0uX.x, b0uX.y, b0uX.z, b0uY.x, b0uY.y, b0uY.z, b0uZ.x, b0uZ.y, b0uZ.z, b0hs.x, b0hs.y, b0hs.z, + b1c.x, b1c.y, b1c.z, b1uX.x, b1uX.y, b1uX.z, b1uY.x, b1uY.y, b1uY.z, b1uZ.x, b1uZ.y, b1uZ.z, b1hs.x, b1hs.y, b1hs.z); + } + + /** + * Test whether two oriented boxes given via their center position, orientation and half-size, intersect. + *

+ * The orientation of a box is given as three unit vectors spanning the local orthonormal basis of the box. + *

+ * The size is given as the half-size along each of the unit vectors defining the orthonormal basis. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 4.4.1 "OBB-OBB Intersection" + * + * @param b0cX + * the x coordinate of the center of the first box + * @param b0cY + * the y coordinate of the center of the first box + * @param b0cZ + * the z coordinate of the center of the first box + * @param b0uXx + * the x coordinate of the local X unit vector of the first box + * @param b0uXy + * the y coordinate of the local X unit vector of the first box + * @param b0uXz + * the z coordinate of the local X unit vector of the first box + * @param b0uYx + * the x coordinate of the local Y unit vector of the first box + * @param b0uYy + * the y coordinate of the local Y unit vector of the first box + * @param b0uYz + * the z coordinate of the local Y unit vector of the first box + * @param b0uZx + * the x coordinate of the local Z unit vector of the first box + * @param b0uZy + * the y coordinate of the local Z unit vector of the first box + * @param b0uZz + * the z coordinate of the local Z unit vector of the first box + * @param b0hsX + * the half-size of the first box along its local X axis + * @param b0hsY + * the half-size of the first box along its local Y axis + * @param b0hsZ + * the half-size of the first box along its local Z axis + * @param b1cX + * the x coordinate of the center of the second box + * @param b1cY + * the y coordinate of the center of the second box + * @param b1cZ + * the z coordinate of the center of the second box + * @param b1uXx + * the x coordinate of the local X unit vector of the second box + * @param b1uXy + * the y coordinate of the local X unit vector of the second box + * @param b1uXz + * the z coordinate of the local X unit vector of the second box + * @param b1uYx + * the x coordinate of the local Y unit vector of the second box + * @param b1uYy + * the y coordinate of the local Y unit vector of the second box + * @param b1uYz + * the z coordinate of the local Y unit vector of the second box + * @param b1uZx + * the x coordinate of the local Z unit vector of the second box + * @param b1uZy + * the y coordinate of the local Z unit vector of the second box + * @param b1uZz + * the z coordinate of the local Z unit vector of the second box + * @param b1hsX + * the half-size of the second box along its local X axis + * @param b1hsY + * the half-size of the second box along its local Y axis + * @param b1hsZ + * the half-size of the second box along its local Z axis + * @return true if both boxes intersect; false otherwise + */ + public static boolean testObOb( + float b0cX, float b0cY, float b0cZ, float b0uXx, float b0uXy, float b0uXz, float b0uYx, float b0uYy, float b0uYz, float b0uZx, float b0uZy, float b0uZz, float b0hsX, float b0hsY, float b0hsZ, + float b1cX, float b1cY, float b1cZ, float b1uXx, float b1uXy, float b1uXz, float b1uYx, float b1uYy, float b1uYz, float b1uZx, float b1uZy, float b1uZz, float b1hsX, float b1hsY, float b1hsZ) { + float ra, rb; + // Compute rotation matrix expressing b in a's coordinate frame + float rm00 = b0uXx * b1uXx + b0uYx * b1uYx + b0uZx * b1uZx; + float rm10 = b0uXx * b1uXy + b0uYx * b1uYy + b0uZx * b1uZy; + float rm20 = b0uXx * b1uXz + b0uYx * b1uYz + b0uZx * b1uZz; + float rm01 = b0uXy * b1uXx + b0uYy * b1uYx + b0uZy * b1uZx; + float rm11 = b0uXy * b1uXy + b0uYy * b1uYy + b0uZy * b1uZy; + float rm21 = b0uXy * b1uXz + b0uYy * b1uYz + b0uZy * b1uZz; + float rm02 = b0uXz * b1uXx + b0uYz * b1uYx + b0uZz * b1uZx; + float rm12 = b0uXz * b1uXy + b0uYz * b1uYy + b0uZz * b1uZy; + float rm22 = b0uXz * b1uXz + b0uYz * b1uYz + b0uZz * b1uZz; + // Compute common subexpressions. Add in an epsilon term to + // counteract arithmetic errors when two edges are parallel and + // their cross product is (near) null (see text for details) + float EPSILON = 1E-5f; + float arm00 = Math.abs(rm00) + EPSILON; + float arm01 = Math.abs(rm01) + EPSILON; + float arm02 = Math.abs(rm02) + EPSILON; + float arm10 = Math.abs(rm10) + EPSILON; + float arm11 = Math.abs(rm11) + EPSILON; + float arm12 = Math.abs(rm12) + EPSILON; + float arm20 = Math.abs(rm20) + EPSILON; + float arm21 = Math.abs(rm21) + EPSILON; + float arm22 = Math.abs(rm22) + EPSILON; + // Compute translation vector t + float tx = b1cX - b0cX, ty = b1cY - b0cY, tz = b1cZ - b0cZ; + // Bring translation into a's coordinate frame + float tax = tx * b0uXx + ty * b0uXy + tz * b0uXz; + float tay = tx * b0uYx + ty * b0uYy + tz * b0uYz; + float taz = tx * b0uZx + ty * b0uZy + tz * b0uZz; + // Test axes L = A0, L = A1, L = A2 + ra = b0hsX; + rb = b1hsX * arm00 + b1hsY * arm01 + b1hsZ * arm02; + if (Math.abs(tax) > ra + rb) return false; + ra = b0hsY; + rb = b1hsX * arm10 + b1hsY * arm11 + b1hsZ * arm12; + if (Math.abs(tay) > ra + rb) return false; + ra = b0hsZ; + rb = b1hsX * arm20 + b1hsY * arm21 + b1hsZ * arm22; + if (Math.abs(taz) > ra + rb) return false; + // Test axes L = B0, L = B1, L = B2 + ra = b0hsX * arm00 + b0hsY * arm10 + b0hsZ * arm20; + rb = b1hsX; + if (Math.abs(tax * rm00 + tay * rm10 + taz * rm20) > ra + rb) return false; + ra = b0hsX * arm01 + b0hsY * arm11 + b0hsZ * arm21; + rb = b1hsY; + if (Math.abs(tax * rm01 + tay * rm11 + taz * rm21) > ra + rb) return false; + ra = b0hsX * arm02 + b0hsY * arm12 + b0hsZ * arm22; + rb = b1hsZ; + if (Math.abs(tax * rm02 + tay * rm12 + taz * rm22) > ra + rb) return false; + // Test axis L = A0 x B0 + ra = b0hsY * arm20 + b0hsZ * arm10; + rb = b1hsY * arm02 + b1hsZ * arm01; + if (Math.abs(taz * rm10 - tay * rm20) > ra + rb) return false; + // Test axis L = A0 x B1 + ra = b0hsY * arm21 + b0hsZ * arm11; + rb = b1hsX * arm02 + b1hsZ * arm00; + if (Math.abs(taz * rm11 - tay * rm21) > ra + rb) return false; + // Test axis L = A0 x B2 + ra = b0hsY * arm22 + b0hsZ * arm12; + rb = b1hsX * arm01 + b1hsY * arm00; + if (Math.abs(taz * rm12 - tay * rm22) > ra + rb) return false; + // Test axis L = A1 x B0 + ra = b0hsX * arm20 + b0hsZ * arm00; + rb = b1hsY * arm12 + b1hsZ * arm11; + if (Math.abs(tax * rm20 - taz * rm00) > ra + rb) return false; + // Test axis L = A1 x B1 + ra = b0hsX * arm21 + b0hsZ * arm01; + rb = b1hsX * arm12 + b1hsZ * arm10; + if (Math.abs(tax * rm21 - taz * rm01) > ra + rb) return false; + // Test axis L = A1 x B2 + ra = b0hsX * arm22 + b0hsZ * arm02; + rb = b1hsX * arm11 + b1hsY * arm10; + if (Math.abs(tax * rm22 - taz * rm02) > ra + rb) return false; + // Test axis L = A2 x B0 + ra = b0hsX * arm10 + b0hsY * arm00; + rb = b1hsY * arm22 + b1hsZ * arm21; + if (Math.abs(tay * rm00 - tax * rm10) > ra + rb) return false; + // Test axis L = A2 x B1 + ra = b0hsX * arm11 + b0hsY * arm01; + rb = b1hsX * arm22 + b1hsZ * arm20; + if (Math.abs(tay * rm01 - tax * rm11) > ra + rb) return false; + // Test axis L = A2 x B2 + ra = b0hsX * arm12 + b0hsY * arm02; + rb = b1hsX * arm21 + b1hsY * arm20; + if (Math.abs(tay * rm02 - tax * rm12) > ra + rb) return false; + // Since no separating axis is found, the OBBs must be intersecting + return true; + } + + /** + * Test whether the one sphere with center (aX, aY, aZ) and square radius radiusSquaredA intersects the other + * sphere with center (bX, bY, bZ) and square radius radiusSquaredB, and store the center of the circle of + * intersection in the (x, y, z) components of the supplied vector and the radius of that circle in the w component. + *

+ * The normal vector of the circle of intersection can simply be obtained by subtracting the center of either sphere from the other. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param aX + * the x coordinate of the first sphere's center + * @param aY + * the y coordinate of the first sphere's center + * @param aZ + * the z coordinate of the first sphere's center + * @param radiusSquaredA + * the square of the first sphere's radius + * @param bX + * the x coordinate of the second sphere's center + * @param bY + * the y coordinate of the second sphere's center + * @param bZ + * the z coordinate of the second sphere's center + * @param radiusSquaredB + * the square of the second sphere's radius + * @param centerAndRadiusOfIntersectionCircle + * will hold the center of the circle of intersection in the (x, y, z) components and the radius in the w component + * @return true iff both spheres intersect; false otherwise + */ + public static boolean intersectSphereSphere( + float aX, float aY, float aZ, float radiusSquaredA, + float bX, float bY, float bZ, float radiusSquaredB, + Vector4f centerAndRadiusOfIntersectionCircle) { + float dX = bX - aX, dY = bY - aY, dZ = bZ - aZ; + float distSquared = dX * dX + dY * dY + dZ * dZ; + float h = 0.5f + (radiusSquaredA - radiusSquaredB) / distSquared; + float r_i = radiusSquaredA - h * h * distSquared; + if (r_i >= 0.0f) { + centerAndRadiusOfIntersectionCircle.x = aX + h * dX; + centerAndRadiusOfIntersectionCircle.y = aY + h * dY; + centerAndRadiusOfIntersectionCircle.z = aZ + h * dZ; + centerAndRadiusOfIntersectionCircle.w = (float) Math.sqrt(r_i); + return true; + } + return false; + } + + /** + * Test whether the one sphere with center centerA and square radius radiusSquaredA intersects the other + * sphere with center centerB and square radius radiusSquaredB, and store the center of the circle of + * intersection in the (x, y, z) components of the supplied vector and the radius of that circle in the w component. + *

+ * The normal vector of the circle of intersection can simply be obtained by subtracting the center of either sphere from the other. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param centerA + * the first sphere's center + * @param radiusSquaredA + * the square of the first sphere's radius + * @param centerB + * the second sphere's center + * @param radiusSquaredB + * the square of the second sphere's radius + * @param centerAndRadiusOfIntersectionCircle + * will hold the center of the circle of intersection in the (x, y, z) components and the radius in the w component + * @return true iff both spheres intersect; false otherwise + */ + public static boolean intersectSphereSphere(Vector3fc centerA, float radiusSquaredA, Vector3fc centerB, float radiusSquaredB, Vector4f centerAndRadiusOfIntersectionCircle) { + return intersectSphereSphere(centerA.x(), centerA.y(), centerA.z(), radiusSquaredA, centerB.x(), centerB.y(), centerB.z(), radiusSquaredB, centerAndRadiusOfIntersectionCircle); + } + + /** + * Test whether the given sphere with center (sX, sY, sZ) intersects the triangle given by its three vertices, and if they intersect + * store the point of intersection into result. + *

+ * This method also returns whether the point of intersection is on one of the triangle's vertices, edges or on the face. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.2.7 "Testing Sphere Against Triangle" + * + * @param sX + * the x coordinate of the sphere's center + * @param sY + * the y coordinate of the sphere's center + * @param sZ + * the z coordinate of the sphere's center + * @param sR + * the sphere's radius + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v0Z + * the z coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v1Z + * the z coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @param v2Z + * the z coordinate of the third vertex of the triangle + * @param result + * will hold the point of intersection + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} or 0 + */ + public static int intersectSphereTriangle( + float sX, float sY, float sZ, float sR, + float v0X, float v0Y, float v0Z, + float v1X, float v1Y, float v1Z, + float v2X, float v2Y, float v2Z, + Vector3f result) { + int closest = findClosestPointOnTriangle(v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z, sX, sY, sZ, result); + float vX = result.x - sX, vY = result.y - sY, vZ = result.z - sZ; + float dot = vX * vX + vY * vY + vZ * vZ; + if (dot <= sR * sR) { + return closest; + } + return 0; + } + + /** + * Test whether the one sphere with center (aX, aY, aZ) and square radius radiusSquaredA intersects the other + * sphere with center (bX, bY, bZ) and square radius radiusSquaredB. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param aX + * the x coordinate of the first sphere's center + * @param aY + * the y coordinate of the first sphere's center + * @param aZ + * the z coordinate of the first sphere's center + * @param radiusSquaredA + * the square of the first sphere's radius + * @param bX + * the x coordinate of the second sphere's center + * @param bY + * the y coordinate of the second sphere's center + * @param bZ + * the z coordinate of the second sphere's center + * @param radiusSquaredB + * the square of the second sphere's radius + * @return true iff both spheres intersect; false otherwise + */ + public static boolean testSphereSphere( + float aX, float aY, float aZ, float radiusSquaredA, + float bX, float bY, float bZ, float radiusSquaredB) { + float dX = bX - aX, dY = bY - aY, dZ = bZ - aZ; + float distSquared = dX * dX + dY * dY + dZ * dZ; + float h = 0.5f + (radiusSquaredA - radiusSquaredB) / distSquared; + float r_i = radiusSquaredA - h * h * distSquared; + return r_i >= 0.0f; + } + + /** + * Test whether the one sphere with center centerA and square radius radiusSquaredA intersects the other + * sphere with center centerB and square radius radiusSquaredB. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param centerA + * the first sphere's center + * @param radiusSquaredA + * the square of the first sphere's radius + * @param centerB + * the second sphere's center + * @param radiusSquaredB + * the square of the second sphere's radius + * @return true iff both spheres intersect; false otherwise + */ + public static boolean testSphereSphere(Vector3fc centerA, float radiusSquaredA, Vector3fc centerB, float radiusSquaredB) { + return testSphereSphere(centerA.x(), centerA.y(), centerA.z(), radiusSquaredA, centerB.x(), centerB.y(), centerB.z(), radiusSquaredB); + } + + /** + * Determine the signed distance of the given point (pointX, pointY, pointZ) to the plane specified via its general plane equation + * a*x + b*y + c*z + d = 0. + * + * @param pointX + * the x coordinate of the point + * @param pointY + * the y coordinate of the point + * @param pointZ + * the z coordinate of the point + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return the distance between the point and the plane + */ + public static float distancePointPlane(float pointX, float pointY, float pointZ, float a, float b, float c, float d) { + float denom = (float) Math.sqrt(a * a + b * b + c * c); + return (a * pointX + b * pointY + c * pointZ + d) / denom; + } + + /** + * Determine the signed distance of the given point (pointX, pointY, pointZ) to the plane of the triangle specified by its three points + * (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z). + *

+ * If the point lies on the front-facing side of the triangle's plane, that is, if the triangle has counter-clockwise winding order + * as seen from the point, then this method returns a positive number. + * + * @param pointX + * the x coordinate of the point + * @param pointY + * the y coordinate of the point + * @param pointZ + * the z coordinate of the point + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v0Z + * the z coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v1Z + * the z coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @param v2Z + * the z coordinate of the third vertex of the triangle + * @return the signed distance between the point and the plane of the triangle + */ + public static float distancePointPlane(float pointX, float pointY, float pointZ, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z) { + float v1Y0Y = v1Y - v0Y; + float v2Z0Z = v2Z - v0Z; + float v2Y0Y = v2Y - v0Y; + float v1Z0Z = v1Z - v0Z; + float v2X0X = v2X - v0X; + float v1X0X = v1X - v0X; + float a = v1Y0Y * v2Z0Z - v2Y0Y * v1Z0Z; + float b = v1Z0Z * v2X0X - v2Z0Z * v1X0X; + float c = v1X0X * v2Y0Y - v2X0X * v1Y0Y; + float d = -(a * v0X + b * v0Y + c * v0Z); + return distancePointPlane(pointX, pointY, pointZ, a, b, c, d); + } + + /** + * Test whether the ray with given origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) intersects the plane + * containing the given point (pointX, pointY, pointZ) and having the normal (normalX, normalY, normalZ), and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the plane, because it is either parallel to the plane or its direction points + * away from the plane or the ray's origin is on the negative side of the plane (i.e. the plane's normal points away from the ray's origin). + *

+ * Reference: https://www.siggraph.org/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param pointX + * the x coordinate of a point on the plane + * @param pointY + * the y coordinate of a point on the plane + * @param pointZ + * the z coordinate of a point on the plane + * @param normalX + * the x coordinate of the plane's normal + * @param normalY + * the y coordinate of the plane's normal + * @param normalZ + * the z coordinate of the plane's normal + * @param epsilon + * some small epsilon for when the ray is parallel to the plane + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the plane; -1.0 otherwise + */ + public static float intersectRayPlane(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float pointX, float pointY, float pointZ, float normalX, float normalY, float normalZ, float epsilon) { + float denom = normalX * dirX + normalY * dirY + normalZ * dirZ; + if (denom < epsilon) { + float t = ((pointX - originX) * normalX + (pointY - originY) * normalY + (pointZ - originZ) * normalZ) / denom; + if (t >= 0.0f) + return t; + } + return -1.0f; + } + + /** + * Test whether the ray with given origin and direction dir intersects the plane + * containing the given point and having the given normal, and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the plane, because it is either parallel to the plane or its direction points + * away from the plane or the ray's origin is on the negative side of the plane (i.e. the plane's normal points away from the ray's origin). + *

+ * Reference: https://www.siggraph.org/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param point + * a point on the plane + * @param normal + * the plane's normal + * @param epsilon + * some small epsilon for when the ray is parallel to the plane + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the plane; -1.0 otherwise + */ + public static float intersectRayPlane(Vector3fc origin, Vector3fc dir, Vector3fc point, Vector3fc normal, float epsilon) { + return intersectRayPlane(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), point.x(), point.y(), point.z(), normal.x(), normal.y(), normal.z(), epsilon); + } + + /** + * Test whether the ray with given origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) intersects the plane + * given as the general plane equation a*x + b*y + c*z + d = 0, and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the plane, because it is either parallel to the plane or its direction points + * away from the plane or the ray's origin is on the negative side of the plane (i.e. the plane's normal points away from the ray's origin). + *

+ * Reference: https://www.siggraph.org/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param epsilon + * some small epsilon for when the ray is parallel to the plane + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the plane; -1.0 otherwise + */ + public static float intersectRayPlane(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float a, float b, float c, float d, float epsilon) { + float denom = a * dirX + b * dirY + c * dirZ; + if (denom < 0.0f) { + float t = -(a * originX + b * originY + c * originZ + d) / denom; + if (t >= 0.0f) + return t; + } + return -1.0f; + } + + /** + * Test whether the axis-aligned box with minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ) + * intersects the sphere with the given center (centerX, centerY, centerZ) and square radius radiusSquared. + *

+ * Reference: http://stackoverflow.com + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the z coordinate of the maximum corner of the axis-aligned box + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radiusSquared + * the square of the sphere's radius + * @return true iff the axis-aligned box intersects the sphere; false otherwise + */ + public static boolean testAabSphere( + float minX, float minY, float minZ, + float maxX, float maxY, float maxZ, + float centerX, float centerY, float centerZ, float radiusSquared) { + float radius2 = radiusSquared; + if (centerX < minX) { + float d = (centerX - minX); + radius2 -= d * d; + } else if (centerX > maxX) { + float d = (centerX - maxX); + radius2 -= d * d; + } + if (centerY < minY) { + float d = (centerY - minY); + radius2 -= d * d; + } else if (centerY > maxY) { + float d = (centerY - maxY); + radius2 -= d * d; + } + if (centerZ < minZ) { + float d = (centerZ - minZ); + radius2 -= d * d; + } else if (centerZ > maxZ) { + float d = (centerZ - maxZ); + radius2 -= d * d; + } + return radius2 >= 0.0f; + } + + /** + * Test whether the axis-aligned box with minimum corner min and maximum corner max + * intersects the sphere with the given center and square radius radiusSquared. + *

+ * Reference: http://stackoverflow.com + * + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param center + * the sphere's center + * @param radiusSquared + * the squared of the sphere's radius + * @return true iff the axis-aligned box intersects the sphere; false otherwise + */ + public static boolean testAabSphere(Vector3fc min, Vector3fc max, Vector3fc center, float radiusSquared) { + return testAabSphere(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), center.x(), center.y(), center.z(), radiusSquared); + } + + /** + * Find the point on the given plane which is closest to the specified point (pX, pY, pZ) and store the result in result. + * + * @param aX + * the x coordinate of one point on the plane + * @param aY + * the y coordinate of one point on the plane + * @param aZ + * the z coordinate of one point on the plane + * @param nX + * the x coordinate of the unit normal of the plane + * @param nY + * the y coordinate of the unit normal of the plane + * @param nZ + * the z coordinate of the unit normal of the plane + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the z coordinate of the point + * @param result + * will hold the result + * @return result + */ + public static Vector3f findClosestPointOnPlane(float aX, float aY, float aZ, float nX, float nY, float nZ, float pX, float pY, float pZ, Vector3f result) { + float d = -(nX * aX + nY * aY + nZ * aZ); + float t = nX * pX + nY * pY + nZ * pZ - d; + result.x = pX - t * nX; + result.y = pY - t * nY; + result.z = pZ - t * nZ; + return result; + } + + /** + * Find the point on the given line segment which is closest to the specified point (pX, pY, pZ), and store the result in result. + * + * @param aX + * the x coordinate of the first end point of the line segment + * @param aY + * the y coordinate of the first end point of the line segment + * @param aZ + * the z coordinate of the first end point of the line segment + * @param bX + * the x coordinate of the second end point of the line segment + * @param bY + * the y coordinate of the second end point of the line segment + * @param bZ + * the z coordinate of the second end point of the line segment + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the z coordinate of the point + * @param result + * will hold the result + * @return result + */ + public static Vector3f findClosestPointOnLineSegment(float aX, float aY, float aZ, float bX, float bY, float bZ, float pX, float pY, float pZ, Vector3f result) { + float abX = bX - aX, abY = bY - aY, abZ = bZ - aZ; + float t = ((pX - aX) * abX + (pY - aY) * abY + (pZ - aZ) * abZ) / (abX * abX + abY * abY + abZ * abZ); + if (t < 0.0f) t = 0.0f; + if (t > 1.0f) t = 1.0f; + result.x = aX + t * abX; + result.y = aY + t * abY; + result.z = aZ + t * abZ; + return result; + } + + /** + * Find the closest points on the two line segments, store the point on the first line segment in resultA and + * the point on the second line segment in resultB, and return the square distance between both points. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.9 "Closest Points of Two Line Segments" + * + * @param a0X + * the x coordinate of the first line segment's first end point + * @param a0Y + * the y coordinate of the first line segment's first end point + * @param a0Z + * the z coordinate of the first line segment's first end point + * @param a1X + * the x coordinate of the first line segment's second end point + * @param a1Y + * the y coordinate of the first line segment's second end point + * @param a1Z + * the z coordinate of the first line segment's second end point + * @param b0X + * the x coordinate of the second line segment's first end point + * @param b0Y + * the y coordinate of the second line segment's first end point + * @param b0Z + * the z coordinate of the second line segment's first end point + * @param b1X + * the x coordinate of the second line segment's second end point + * @param b1Y + * the y coordinate of the second line segment's second end point + * @param b1Z + * the z coordinate of the second line segment's second end point + * @param resultA + * will hold the point on the first line segment + * @param resultB + * will hold the point on the second line segment + * @return the square distance between the two closest points + */ + public static float findClosestPointsLineSegments( + float a0X, float a0Y, float a0Z, float a1X, float a1Y, float a1Z, + float b0X, float b0Y, float b0Z, float b1X, float b1Y, float b1Z, + Vector3f resultA, Vector3f resultB) { + float d1x = a1X - a0X, d1y = a1Y - a0Y, d1z = a1Z - a0Z; + float d2x = b1X - b0X, d2y = b1Y - b0Y, d2z = b1Z - b0Z; + float rX = a0X - b0X, rY = a0Y - b0Y, rZ = a0Z - b0Z; + float a = d1x * d1x + d1y * d1y + d1z * d1z; + float e = d2x * d2x + d2y * d2y + d2z * d2z; + float f = d2x * rX + d2y * rY + d2z * rZ; + float EPSILON = 1E-5f; + float s, t; + if (a <= EPSILON && e <= EPSILON) { + // Both segments degenerate into points + resultA.set(a0X, a0Y, a0Z); + resultB.set(b0X, b0Y, b0Z); + return resultA.dot(resultB); + } + if (a <= EPSILON) { + // First segment degenerates into a point + s = 0.0f; + t = f / e; + t = Math.min(Math.max(t, 0.0f), 1.0f); + } else { + float c = d1x * rX + d1y * rY + d1z * rZ; + if (e <= EPSILON) { + // Second segment degenerates into a point + t = 0.0f; + s = Math.min(Math.max(-c / a, 0.0f), 1.0f); + } else { + // The general nondegenerate case starts here + float b = d1x * d2x + d1y * d2y + d1z * d2z; + float denom = a * e - b * b; + // If segments not parallel, compute closest point on L1 to L2 and + // clamp to segment S1. Else pick arbitrary s (here 0) + if (denom != 0.0) + s = Math.min(Math.max((b*f - c*e) / denom, 0.0f), 1.0f); + else + s = 0.0f; + // Compute point on L2 closest to S1(s) using + // t = Dot((P1 + D1*s) - P2,D2) / Dot(D2,D2) = (b*s + f) / e + t = (b * s + f) / e; + // If t in [0,1] done. Else clamp t, recompute s for the new value + // of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a + // and clamp s to [0, 1] + if (t < 0.0) { + t = 0.0f; + s = Math.min(Math.max(-c / a, 0.0f), 1.0f); + } else if (t > 1.0) { + t = 1.0f; + s = Math.min(Math.max((b - c) / a, 0.0f), 1.0f); + } + } + } + resultA.set(a0X + d1x * s, a0Y + d1y * s, a0Z + d1z * s); + resultB.set(b0X + d2x * t, b0Y + d2y * t, b0Z + d2z * t); + float dX = resultA.x - resultB.x, dY = resultA.y - resultB.y, dZ = resultA.z - resultB.z; + return dX*dX + dY*dY + dZ*dZ; + } + + /** + * Find the closest points on a line segment and a triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.10 "Closest Points of a Line Segment and a Triangle" + * + * @param aX + * the x coordinate of the line segment's first end point + * @param aY + * the y coordinate of the line segment's first end point + * @param aZ + * the z coordinate of the line segment's first end point + * @param bX + * the x coordinate of the line segment's second end point + * @param bY + * the y coordinate of the line segment's second end point + * @param bZ + * the z coordinate of the line segment's second end point + * @param v0X + * the x coordinate of the triangle's first vertex + * @param v0Y + * the y coordinate of the triangle's first vertex + * @param v0Z + * the z coordinate of the triangle's first vertex + * @param v1X + * the x coordinate of the triangle's second vertex + * @param v1Y + * the y coordinate of the triangle's second vertex + * @param v1Z + * the z coordinate of the triangle's second vertex + * @param v2X + * the x coordinate of the triangle's third vertex + * @param v2Y + * the y coordinate of the triangle's third vertex + * @param v2Z + * the z coordinate of the triangle's third vertex + * @param lineSegmentResult + * will hold the closest point on the line segment + * @param triangleResult + * will hold the closest point on the triangle + * @return the square distance of the closest points + */ + public static float findClosestPointsLineSegmentTriangle( + float aX, float aY, float aZ, float bX, float bY, float bZ, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, + Vector3f lineSegmentResult, Vector3f triangleResult) { + float min, d; + float minlsX, minlsY, minlsZ, mintX, mintY, mintZ; + // AB -> V0V1 + d = findClosestPointsLineSegments(aX, aY, aZ, bX, bY, bZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, lineSegmentResult, triangleResult); + min = d; + minlsX = lineSegmentResult.x; minlsY = lineSegmentResult.y; minlsZ = lineSegmentResult.z; + mintX = triangleResult.x; mintY = triangleResult.y; mintZ = triangleResult.z; + // AB -> V1V2 + d = findClosestPointsLineSegments(aX, aY, aZ, bX, bY, bZ, v1X, v1Y, v1Z, v2X, v2Y, v2Z, lineSegmentResult, triangleResult); + if (d < min) { + min = d; + minlsX = lineSegmentResult.x; minlsY = lineSegmentResult.y; minlsZ = lineSegmentResult.z; + mintX = triangleResult.x; mintY = triangleResult.y; mintZ = triangleResult.z; + } + // AB -> V2V0 + d = findClosestPointsLineSegments(aX, aY, aZ, bX, bY, bZ, v2X, v2Y, v2Z, v0X, v0Y, v0Z, lineSegmentResult, triangleResult); + if (d < min) { + min = d; + minlsX = lineSegmentResult.x; minlsY = lineSegmentResult.y; minlsZ = lineSegmentResult.z; + mintX = triangleResult.x; mintY = triangleResult.y; mintZ = triangleResult.z; + } + // segment endpoint A and plane of triangle (when A projects inside V0V1V2) + boolean computed = false; + float a = Float.NaN, b = Float.NaN, c = Float.NaN, nd = Float.NaN; + if (testPointInTriangle(aX, aY, aZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z)) { + float v1Y0Y = v1Y - v0Y; + float v2Z0Z = v2Z - v0Z; + float v2Y0Y = v2Y - v0Y; + float v1Z0Z = v1Z - v0Z; + float v2X0X = v2X - v0X; + float v1X0X = v1X - v0X; + a = v1Y0Y * v2Z0Z - v2Y0Y * v1Z0Z; + b = v1Z0Z * v2X0X - v2Z0Z * v1X0X; + c = v1X0X * v2Y0Y - v2X0X * v1Y0Y; + computed = true; + float invLen = Math.invsqrt(a*a + b*b + c*c); + a *= invLen; b *= invLen; c *= invLen; + nd = -(a * v0X + b * v0Y + c * v0Z); + d = (a * aX + b * aY + c * aZ + nd); + float l = d; + d *= d; + if (d < min) { + min = d; + minlsX = aX; minlsY = aY; minlsZ = aZ; + mintX = aX - a*l; mintY = aY - b*l; mintZ = aZ - c*l; + } + } + // segment endpoint B and plane of triangle (when B projects inside V0V1V2) + if (testPointInTriangle(bX, bY, bZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z)) { + if (!computed) { + float v1Y0Y = v1Y - v0Y; + float v2Z0Z = v2Z - v0Z; + float v2Y0Y = v2Y - v0Y; + float v1Z0Z = v1Z - v0Z; + float v2X0X = v2X - v0X; + float v1X0X = v1X - v0X; + a = v1Y0Y * v2Z0Z - v2Y0Y * v1Z0Z; + b = v1Z0Z * v2X0X - v2Z0Z * v1X0X; + c = v1X0X * v2Y0Y - v2X0X * v1Y0Y; + float invLen = Math.invsqrt(a*a + b*b + c*c); + a *= invLen; b *= invLen; c *= invLen; + nd = -(a * v0X + b * v0Y + c * v0Z); + } + d = (a * bX + b * bY + c * bZ + nd); + float l = d; + d *= d; + if (d < min) { + min = d; + minlsX = bX; minlsY = bY; minlsZ = bZ; + mintX = bX - a*l; mintY = bY - b*l; mintZ = bZ - c*l; + } + } + lineSegmentResult.set(minlsX, minlsY, minlsZ); + triangleResult.set(mintX, mintY, mintZ); + return min; + } + + /** + * Determine the closest point on the triangle with the given vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z), (v2X, v2Y, v2Z) + * between that triangle and the given point (pX, pY, pZ) and store that point into the given result. + *

+ * Additionally, this method returns whether the closest point is a vertex ({@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}) + * of the triangle, lies on an edge ({@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20}) + * or on the {@link #POINT_ON_TRIANGLE_FACE face} of the triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.5 "Closest Point on Triangle to Point" + * + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v0Z + * the z coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v1Z + * the z coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @param v2Z + * the z coordinate of the third vertex of the triangle + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the y coordinate of the point + * @param result + * will hold the closest point + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} + */ + public static int findClosestPointOnTriangle( + float v0X, float v0Y, float v0Z, + float v1X, float v1Y, float v1Z, + float v2X, float v2Y, float v2Z, + float pX, float pY, float pZ, + Vector3f result) { + float abX = v1X - v0X, abY = v1Y - v0Y, abZ = v1Z - v0Z; + float acX = v2X - v0X, acY = v2Y - v0Y, acZ = v2Z - v0Z; + float apX = pX - v0X, apY = pY - v0Y, apZ = pZ - v0Z; + float d1 = abX * apX + abY * apY + abZ * apZ; + float d2 = acX * apX + acY * apY + acZ * apZ; + if (d1 <= 0.0f && d2 <= 0.0f) { + result.x = v0X; + result.y = v0Y; + result.z = v0Z; + return POINT_ON_TRIANGLE_VERTEX_0; + } + float bpX = pX - v1X, bpY = pY - v1Y, bpZ = pZ - v1Z; + float d3 = abX * bpX + abY * bpY + abZ * bpZ; + float d4 = acX * bpX + acY * bpY + acZ * bpZ; + if (d3 >= 0.0f && d4 <= d3) { + result.x = v1X; + result.y = v1Y; + result.z = v1Z; + return POINT_ON_TRIANGLE_VERTEX_1; + } + float vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) { + float v = d1 / (d1 - d3); + result.x = v0X + v * abX; + result.y = v0Y + v * abY; + result.z = v0Z + v * abZ; + return POINT_ON_TRIANGLE_EDGE_01; + } + float cpX = pX - v2X, cpY = pY - v2Y, cpZ = pZ - v2Z; + float d5 = abX * cpX + abY * cpY + abZ * cpZ; + float d6 = acX * cpX + acY * cpY + acZ * cpZ; + if (d6 >= 0.0f && d5 <= d6) { + result.x = v2X; + result.y = v2Y; + result.z = v2Z; + return POINT_ON_TRIANGLE_VERTEX_2; + } + float vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) { + float w = d2 / (d2 - d6); + result.x = v0X + w * acX; + result.y = v0Y + w * acY; + result.z = v0Z + w * acZ; + return POINT_ON_TRIANGLE_EDGE_20; + } + float va = d3 * d6 - d5 * d4; + if (va <= 0.0f && d4 - d3 >= 0.0f && d5 - d6 >= 0.0f) { + float w = (d4 - d3) / (d4 - d3 + d5 - d6); + result.x = v1X + w * (v2X - v1X); + result.y = v1Y + w * (v2Y - v1Y); + result.z = v1Z + w * (v2Z - v1Z); + return POINT_ON_TRIANGLE_EDGE_12; + } + float denom = 1.0f / (va + vb + vc); + float v = vb * denom; + float w = vc * denom; + result.x = v0X + abX * v + acX * w; + result.y = v0Y + abY * v + acY * w; + result.z = v0Z + abZ * v + acZ * w; + return POINT_ON_TRIANGLE_FACE; + } + + /** + * Determine the closest point on the triangle with the vertices v0, v1, v2 + * between that triangle and the given point p and store that point into the given result. + *

+ * Additionally, this method returns whether the closest point is a vertex ({@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}) + * of the triangle, lies on an edge ({@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20}) + * or on the {@link #POINT_ON_TRIANGLE_FACE face} of the triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.5 "Closest Point on Triangle to Point" + * + * @param v0 + * the first vertex of the triangle + * @param v1 + * the second vertex of the triangle + * @param v2 + * the third vertex of the triangle + * @param p + * the point + * @param result + * will hold the closest point + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} + */ + public static int findClosestPointOnTriangle(Vector3fc v0, Vector3fc v1, Vector3fc v2, Vector3fc p, Vector3f result) { + return findClosestPointOnTriangle(v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), p.x(), p.y(), p.z(), result); + } + + /** + * Find the point on a given rectangle, specified via three of its corners, which is closest to the specified point + * (pX, pY, pZ) and store the result into res. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.4.2 "Closest Point on 3D Rectangle to Point" + * + * @param aX + * the x coordinate of the first corner point of the rectangle + * @param aY + * the y coordinate of the first corner point of the rectangle + * @param aZ + * the z coordinate of the first corner point of the rectangle + * @param bX + * the x coordinate of the second corner point of the rectangle + * @param bY + * the y coordinate of the second corner point of the rectangle + * @param bZ + * the z coordinate of the second corner point of the rectangle + * @param cX + * the x coordinate of the third corner point of the rectangle + * @param cY + * the y coordinate of the third corner point of the rectangle + * @param cZ + * the z coordinate of the third corner point of the rectangle + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the z coordinate of the point + * @param res + * will hold the result + * @return res + */ + public static Vector3f findClosestPointOnRectangle( + float aX, float aY, float aZ, + float bX, float bY, float bZ, + float cX, float cY, float cZ, + float pX, float pY, float pZ, Vector3f res) { + float abX = bX - aX, abY = bY - aY, abZ = bZ - aZ; + float acX = cX - aX, acY = cY - aY, acZ = cZ - aZ; + float dX = pX - aX, dY = pY - aY, dZ = pZ - aZ; + float qX = aX, qY = aY, qZ = aZ; + float dist = dX * abX + dY * abY + dZ * abZ; + float maxdist = abX * abX + abY * abY + abZ * abZ; + if (dist >= maxdist) { + qX += abX; + qY += abY; + qZ += abZ; + } else if (dist > 0.0f) { + qX += (dist / maxdist) * abX; + qY += (dist / maxdist) * abY; + qZ += (dist / maxdist) * abZ; + } + dist = dX * acX + dY * acY + dZ * acZ; + maxdist = acX * acX + acY * acY + acZ * acZ; + if (dist >= maxdist) { + qX += acX; + qY += acY; + qZ += acZ; + } else if (dist > 0.0f) { + qX += (dist / maxdist) * acX; + qY += (dist / maxdist) * acY; + qZ += (dist / maxdist) * acZ; + } + res.x = qX; + res.y = qY; + res.z = qZ; + return res; + } + + /** + * Determine the point of intersection between a sphere with the given center (centerX, centerY, centerZ) and radius moving + * with the given velocity (velX, velY, velZ) and the triangle specified via its three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z), (v2X, v2Y, v2Z). + *

+ * The vertices of the triangle must be specified in counter-clockwise winding order. + *

+ * An intersection is only considered if the time of intersection is smaller than the given maxT value. + *

+ * Reference: Improved Collision detection and Response + * + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radius + * the radius of the sphere + * @param velX + * the x component of the velocity of the sphere + * @param velY + * the y component of the velocity of the sphere + * @param velZ + * the z component of the velocity of the sphere + * @param v0X + * the x coordinate of the first triangle vertex + * @param v0Y + * the y coordinate of the first triangle vertex + * @param v0Z + * the z coordinate of the first triangle vertex + * @param v1X + * the x coordinate of the second triangle vertex + * @param v1Y + * the y coordinate of the second triangle vertex + * @param v1Z + * the z coordinate of the second triangle vertex + * @param v2X + * the x coordinate of the third triangle vertex + * @param v2Y + * the y coordinate of the third triangle vertex + * @param v2Z + * the z coordinate of the third triangle vertex + * @param epsilon + * a small epsilon when testing spheres that move almost parallel to the triangle + * @param maxT + * the maximum intersection time + * @param pointAndTime + * iff the moving sphere and the triangle intersect, this will hold the point of intersection in the (x, y, z) components + * and the time of intersection in the w component + * @return {@link #POINT_ON_TRIANGLE_FACE} if the intersection point lies on the triangle's face, + * or {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1} or {@link #POINT_ON_TRIANGLE_VERTEX_2} if the intersection point is a vertex, + * or {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12} or {@link #POINT_ON_TRIANGLE_EDGE_20} if the intersection point lies on an edge; + * or 0 if no intersection + */ + public static int intersectSweptSphereTriangle( + float centerX, float centerY, float centerZ, float radius, float velX, float velY, float velZ, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, + float epsilon, float maxT, Vector4f pointAndTime) { + float v10X = v1X - v0X; + float v10Y = v1Y - v0Y; + float v10Z = v1Z - v0Z; + float v20X = v2X - v0X; + float v20Y = v2Y - v0Y; + float v20Z = v2Z - v0Z; + // build triangle plane + float a = v10Y * v20Z - v20Y * v10Z; + float b = v10Z * v20X - v20Z * v10X; + float c = v10X * v20Y - v20X * v10Y; + float d = -(a * v0X + b * v0Y + c * v0Z); + float invLen = Math.invsqrt(a * a + b * b + c * c); + float signedDist = (a * centerX + b * centerY + c * centerZ + d) * invLen; + float dot = (a * velX + b * velY + c * velZ) * invLen; + if (dot < epsilon && dot > -epsilon) + return 0; + float pt0 = (radius - signedDist) / dot; + if (pt0 > maxT) + return 0; + float pt1 = (-radius - signedDist) / dot; + float p0X = centerX - radius * a * invLen + velX * pt0; + float p0Y = centerY - radius * b * invLen + velY * pt0; + float p0Z = centerZ - radius * c * invLen + velZ * pt0; + boolean insideTriangle = testPointInTriangle(p0X, p0Y, p0Z, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z); + if (insideTriangle) { + pointAndTime.x = p0X; + pointAndTime.y = p0Y; + pointAndTime.z = p0Z; + pointAndTime.w = pt0; + return POINT_ON_TRIANGLE_FACE; + } + int isect = 0; + float t0 = maxT; + float A = velX * velX + velY * velY + velZ * velZ; + float radius2 = radius * radius; + // test against v0 + float centerV0X = centerX - v0X; + float centerV0Y = centerY - v0Y; + float centerV0Z = centerZ - v0Z; + float B0 = 2.0f * (velX * centerV0X + velY * centerV0Y + velZ * centerV0Z); + float C0 = centerV0X * centerV0X + centerV0Y * centerV0Y + centerV0Z * centerV0Z - radius2; + float root0 = computeLowestRoot(A, B0, C0, t0); + if (root0 < t0) { + pointAndTime.x = v0X; + pointAndTime.y = v0Y; + pointAndTime.z = v0Z; + pointAndTime.w = root0; + t0 = root0; + isect = POINT_ON_TRIANGLE_VERTEX_0; + } + // test against v1 + float centerV1X = centerX - v1X; + float centerV1Y = centerY - v1Y; + float centerV1Z = centerZ - v1Z; + float centerV1Len = centerV1X * centerV1X + centerV1Y * centerV1Y + centerV1Z * centerV1Z; + float B1 = 2.0f * (velX * centerV1X + velY * centerV1Y + velZ * centerV1Z); + float C1 = centerV1Len - radius2; + float root1 = computeLowestRoot(A, B1, C1, t0); + if (root1 < t0) { + pointAndTime.x = v1X; + pointAndTime.y = v1Y; + pointAndTime.z = v1Z; + pointAndTime.w = root1; + t0 = root1; + isect = POINT_ON_TRIANGLE_VERTEX_1; + } + // test against v2 + float centerV2X = centerX - v2X; + float centerV2Y = centerY - v2Y; + float centerV2Z = centerZ - v2Z; + float B2 = 2.0f * (velX * centerV2X + velY * centerV2Y + velZ * centerV2Z); + float C2 = centerV2X * centerV2X + centerV2Y * centerV2Y + centerV2Z * centerV2Z - radius2; + float root2 = computeLowestRoot(A, B2, C2, t0); + if (root2 < t0) { + pointAndTime.x = v2X; + pointAndTime.y = v2Y; + pointAndTime.z = v2Z; + pointAndTime.w = root2; + t0 = root2; + isect = POINT_ON_TRIANGLE_VERTEX_2; + } + float velLen = velX * velX + velY * velY + velZ * velZ; + // test against edge10 + float len10 = v10X * v10X + v10Y * v10Y + v10Z * v10Z; + float baseTo0Len = centerV0X * centerV0X + centerV0Y * centerV0Y + centerV0Z * centerV0Z; + float v10Vel = (v10X * velX + v10Y * velY + v10Z * velZ); + float A10 = len10 * -velLen + v10Vel * v10Vel; + float v10BaseTo0 = v10X * -centerV0X + v10Y * -centerV0Y + v10Z * -centerV0Z; + float velBaseTo0 = velX * -centerV0X + velY * -centerV0Y + velZ * -centerV0Z; + float B10 = len10 * 2 * velBaseTo0 - 2 * v10Vel * v10BaseTo0; + float C10 = len10 * (radius2 - baseTo0Len) + v10BaseTo0 * v10BaseTo0; + float root10 = computeLowestRoot(A10, B10, C10, t0); + float f10 = (v10Vel * root10 - v10BaseTo0) / len10; + if (f10 >= 0.0f && f10 <= 1.0f && root10 < t0) { + pointAndTime.x = v0X + f10 * v10X; + pointAndTime.y = v0Y + f10 * v10Y; + pointAndTime.z = v0Z + f10 * v10Z; + pointAndTime.w = root10; + t0 = root10; + isect = POINT_ON_TRIANGLE_EDGE_01; + } + // test against edge20 + float len20 = v20X * v20X + v20Y * v20Y + v20Z * v20Z; + float v20Vel = (v20X * velX + v20Y * velY + v20Z * velZ); + float A20 = len20 * -velLen + v20Vel * v20Vel; + float v20BaseTo0 = v20X * -centerV0X + v20Y * -centerV0Y + v20Z * -centerV0Z; + float B20 = len20 * 2 * velBaseTo0 - 2 * v20Vel * v20BaseTo0; + float C20 = len20 * (radius2 - baseTo0Len) + v20BaseTo0 * v20BaseTo0; + float root20 = computeLowestRoot(A20, B20, C20, t0); + float f20 = (v20Vel * root20 - v20BaseTo0) / len20; + if (f20 >= 0.0f && f20 <= 1.0f && root20 < pt1) { + pointAndTime.x = v0X + f20 * v20X; + pointAndTime.y = v0Y + f20 * v20Y; + pointAndTime.z = v0Z + f20 * v20Z; + pointAndTime.w = root20; + t0 = root20; + isect = POINT_ON_TRIANGLE_EDGE_20; + } + // test against edge21 + float v21X = v2X - v1X; + float v21Y = v2Y - v1Y; + float v21Z = v2Z - v1Z; + float len21 = v21X * v21X + v21Y * v21Y + v21Z * v21Z; + float baseTo1Len = centerV1Len; + float v21Vel = (v21X * velX + v21Y * velY + v21Z * velZ); + float A21 = len21 * -velLen + v21Vel * v21Vel; + float v21BaseTo1 = v21X * -centerV1X + v21Y * -centerV1Y + v21Z * -centerV1Z; + float velBaseTo1 = velX * -centerV1X + velY * -centerV1Y + velZ * -centerV1Z; + float B21 = len21 * 2 * velBaseTo1 - 2 * v21Vel * v21BaseTo1; + float C21 = len21 * (radius2 - baseTo1Len) + v21BaseTo1 * v21BaseTo1; + float root21 = computeLowestRoot(A21, B21, C21, t0); + float f21 = (v21Vel * root21 - v21BaseTo1) / len21; + if (f21 >= 0.0f && f21 <= 1.0f && root21 < t0) { + pointAndTime.x = v1X + f21 * v21X; + pointAndTime.y = v1Y + f21 * v21Y; + pointAndTime.z = v1Z + f21 * v21Z; + pointAndTime.w = root21; + t0 = root21; + isect = POINT_ON_TRIANGLE_EDGE_12; + } + return isect; + } + + /** + * Compute the lowest root for t in the quadratic equation a*t*t + b*t + c = 0. + *

+ * This is a helper method for {@link #intersectSweptSphereTriangle} + * + * @param a + * the quadratic factor + * @param b + * the linear factor + * @param c + * the constant + * @param maxR + * the maximum expected root + * @return the lowest of the two roots of the quadratic equation; or {@link Float#POSITIVE_INFINITY} + */ + private static float computeLowestRoot(float a, float b, float c, float maxR) { + float determinant = b * b - 4.0f * a * c; + if (determinant < 0.0f) + return Float.POSITIVE_INFINITY; + float sqrtD = (float) Math.sqrt(determinant); + float r1 = (-b - sqrtD) / (2.0f * a); + float r2 = (-b + sqrtD) / (2.0f * a); + if (r1 > r2) { + float temp = r2; + r2 = r1; + r1 = temp; + } + if (r1 > 0.0f && r1 < maxR) { + return r1; + } + if (r2 > 0.0f && r2 < maxR) { + return r2; + } + return Float.POSITIVE_INFINITY; + } + + /** + * Test whether the projection of the given point (pX, pY, pZ) lies inside of the triangle defined by the three vertices + * (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z). + *

+ * Reference: Improved Collision detection and Response + * + * @param pX + * the x coordinate of the point to test + * @param pY + * the y coordinate of the point to test + * @param pZ + * the z coordinate of the point to test + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @return true if the projection of the given point lies inside of the given triangle; false otherwise + */ + public static boolean testPointInTriangle(float pX, float pY, float pZ, float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z) { + float e10X = v1X - v0X; + float e10Y = v1Y - v0Y; + float e10Z = v1Z - v0Z; + float e20X = v2X - v0X; + float e20Y = v2Y - v0Y; + float e20Z = v2Z - v0Z; + float a = e10X * e10X + e10Y * e10Y + e10Z * e10Z; + float b = e10X * e20X + e10Y * e20Y + e10Z * e20Z; + float c = e20X * e20X + e20Y * e20Y + e20Z * e20Z; + float ac_bb = a * c - b * b; + float vpX = pX - v0X; + float vpY = pY - v0Y; + float vpZ = pZ - v0Z; + float d = vpX * e10X + vpY * e10Y + vpZ * e10Z; + float e = vpX * e20X + vpY * e20Y + vpZ * e20Z; + float x = d * c - e * b; + float y = e * a - d * b; + float z = x + y - ac_bb; + return ((Runtime.floatToIntBits(z) & ~(Runtime.floatToIntBits(x) | Runtime.floatToIntBits(y))) & 0x8000000000000000L) != 0L; + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and normalized direction (dirX, dirY, dirZ) + * intersects the given sphere with center (centerX, centerY, centerZ) and square radius radiusSquared, + * and store the values of the parameter t in the ray equation p(t) = origin + t * dir for both points (near + * and far) of intersections into the given result vector. + *

+ * This method returns true for a ray whose origin lies inside the sphere. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's normalized direction + * @param dirY + * the y coordinate of the ray's normalized direction + * @param dirZ + * the z coordinate of the ray's normalized direction + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radiusSquared + * the sphere radius squared + * @param result + * a vector that will contain the values of the parameter t in the ray equation + * p(t) = origin + t * dir for both points (near, far) of intersections with the sphere + * @return true if the ray intersects the sphere; false otherwise + */ + public static boolean intersectRaySphere(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float centerX, float centerY, float centerZ, float radiusSquared, Vector2f result) { + float Lx = centerX - originX; + float Ly = centerY - originY; + float Lz = centerZ - originZ; + float tca = Lx * dirX + Ly * dirY + Lz * dirZ; + float d2 = Lx * Lx + Ly * Ly + Lz * Lz - tca * tca; + if (d2 > radiusSquared) + return false; + float thc = (float) Math.sqrt(radiusSquared - d2); + float t0 = tca - thc; + float t1 = tca + thc; + if (t0 < t1 && t1 >= 0.0f) { + result.x = t0; + result.y = t1; + return true; + } + return false; + } + + /** + * Test whether the ray with the given origin and normalized direction dir + * intersects the sphere with the given center and square radius radiusSquared, + * and store the values of the parameter t in the ray equation p(t) = origin + t * dir for both points (near + * and far) of intersections into the given result vector. + *

+ * This method returns true for a ray whose origin lies inside the sphere. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's normalized direction + * @param center + * the sphere's center + * @param radiusSquared + * the sphere radius squared + * @param result + * a vector that will contain the values of the parameter t in the ray equation + * p(t) = origin + t * dir for both points (near, far) of intersections with the sphere + * @return true if the ray intersects the sphere; false otherwise + */ + public static boolean intersectRaySphere(Vector3fc origin, Vector3fc dir, Vector3fc center, float radiusSquared, Vector2f result) { + return intersectRaySphere(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), center.x(), center.y(), center.z(), radiusSquared, result); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and normalized direction (dirX, dirY, dirZ) + * intersects the given sphere with center (centerX, centerY, centerZ) and square radius radiusSquared. + *

+ * This method returns true for a ray whose origin lies inside the sphere. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's normalized direction + * @param dirY + * the y coordinate of the ray's normalized direction + * @param dirZ + * the z coordinate of the ray's normalized direction + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radiusSquared + * the sphere radius squared + * @return true if the ray intersects the sphere; false otherwise + */ + public static boolean testRaySphere(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float centerX, float centerY, float centerZ, float radiusSquared) { + float Lx = centerX - originX; + float Ly = centerY - originY; + float Lz = centerZ - originZ; + float tca = Lx * dirX + Ly * dirY + Lz * dirZ; + float d2 = Lx * Lx + Ly * Ly + Lz * Lz - tca * tca; + if (d2 > radiusSquared) + return false; + float thc = (float) Math.sqrt(radiusSquared - d2); + float t0 = tca - thc; + float t1 = tca + thc; + return t0 < t1 && t1 >= 0.0f; + } + + /** + * Test whether the ray with the given origin and normalized direction dir + * intersects the sphere with the given center and square radius. + *

+ * This method returns true for a ray whose origin lies inside the sphere. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's normalized direction + * @param center + * the sphere's center + * @param radiusSquared + * the sphere radius squared + * @return true if the ray intersects the sphere; false otherwise + */ + public static boolean testRaySphere(Vector3fc origin, Vector3fc dir, Vector3fc center, float radiusSquared) { + return testRaySphere(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), center.x(), center.y(), center.z(), radiusSquared); + } + + /** + * Test whether the line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the given sphere with center (centerX, centerY, centerZ) and square radius radiusSquared. + *

+ * Reference: http://paulbourke.net/ + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param centerX + * the x coordinate of the sphere's center + * @param centerY + * the y coordinate of the sphere's center + * @param centerZ + * the z coordinate of the sphere's center + * @param radiusSquared + * the sphere radius squared + * @return true if the line segment intersects the sphere; false otherwise + */ + public static boolean testLineSegmentSphere(float p0X, float p0Y, float p0Z, float p1X, float p1Y, float p1Z, + float centerX, float centerY, float centerZ, float radiusSquared) { + float dX = p1X - p0X; + float dY = p1Y - p0Y; + float dZ = p1Z - p0Z; + float nom = (centerX - p0X) * dX + (centerY - p0Y) * dY + (centerZ - p0Z) * dZ; + float den = dX * dX + dY * dY + dZ * dZ; + float u = nom / den; + if (u < 0.0f) { + dX = p0X - centerX; + dY = p0Y - centerY; + dZ = p0Z - centerZ; + } else if (u > 1.0f) { + dX = p1X - centerX; + dY = p1Y - centerY; + dZ = p1Z - centerZ; + } else { // has to be >= 0 and <= 1 + float pX = p0X + u * dX; + float pY = p0Y + u * dY; + float pZ = p0Z + u * dZ; + dX = pX - centerX; + dY = pY - centerY; + dZ = pZ - centerZ; + } + float dist = dX * dX + dY * dY + dZ * dZ; + return dist <= radiusSquared; + } + + /** + * Test whether the line segment with the end points p0 and p1 + * intersects the given sphere with center center and square radius radiusSquared. + *

+ * Reference: http://paulbourke.net/ + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param center + * the sphere's center + * @param radiusSquared + * the sphere radius squared + * @return true if the line segment intersects the sphere; false otherwise + */ + public static boolean testLineSegmentSphere(Vector3fc p0, Vector3fc p1, Vector3fc center, float radiusSquared) { + return testLineSegmentSphere(p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z(), center.x(), center.y(), center.z(), radiusSquared); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the axis-aligned box given as its minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ), + * and return the values of the parameter t in the ray equation p(t) = origin + t * dir of the near and far point of intersection. + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned box. + *

+ * If many boxes need to be tested against the same ray, then the {@link RayAabIntersection} class is likely more efficient. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectRayAab(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector2f) + * @see RayAabIntersection + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the y coordinate of the maximum corner of the axis-aligned box + * @param result + * a vector which will hold the resulting values of the parameter + * t in the ray equation p(t) = origin + t * dir of the near and far point of intersection + * iff the ray intersects the axis-aligned box + * @return true if the given ray intersects the axis-aligned box; false otherwise + */ + public static boolean intersectRayAab(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float minX, float minY, float minZ, float maxX, float maxY, float maxZ, Vector2f result) { + float invDirX = 1.0f / dirX, invDirY = 1.0f / dirY, invDirZ = 1.0f / dirZ; + float tNear, tFar, tymin, tymax, tzmin, tzmax; + if (invDirX >= 0.0f) { + tNear = (minX - originX) * invDirX; + tFar = (maxX - originX) * invDirX; + } else { + tNear = (maxX - originX) * invDirX; + tFar = (minX - originX) * invDirX; + } + if (invDirY >= 0.0f) { + tymin = (minY - originY) * invDirY; + tymax = (maxY - originY) * invDirY; + } else { + tymin = (maxY - originY) * invDirY; + tymax = (minY - originY) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return false; + if (invDirZ >= 0.0f) { + tzmin = (minZ - originZ) * invDirZ; + tzmax = (maxZ - originZ) * invDirZ; + } else { + tzmin = (maxZ - originZ) * invDirZ; + tzmax = (minZ - originZ) * invDirZ; + } + if (tNear > tzmax || tzmin > tFar) + return false; + tNear = tymin > tNear || Float.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Float.isNaN(tFar) ? tymax : tFar; + tNear = tzmin > tNear ? tzmin : tNear; + tFar = tzmax < tFar ? tzmax : tFar; + if (tNear < tFar && tFar >= 0.0f) { + result.x = tNear; + result.y = tFar; + return true; + } + return false; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the axis-aligned box specified as its minimum corner min and maximum corner max, + * and return the values of the parameter t in the ray equation p(t) = origin + t * dir of the near and far point of intersection.. + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned box. + *

+ * If many boxes need to be tested against the same ray, then the {@link RayAabIntersection} class is likely more efficient. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectRayAab(float, float, float, float, float, float, float, float, float, float, float, float, Vector2f) + * @see RayAabIntersection + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param result + * a vector which will hold the resulting values of the parameter + * t in the ray equation p(t) = origin + t * dir of the near and far point of intersection + * iff the ray intersects the axis-aligned box + * @return true if the given ray intersects the axis-aligned box; false otherwise + */ + public static boolean intersectRayAab(Vector3fc origin, Vector3fc dir, Vector3fc min, Vector3fc max, Vector2f result) { + return intersectRayAab(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), result); + } + + /** + * Determine whether the undirected line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the axis-aligned box given as its minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ), + * and return the values of the parameter t in the ray equation p(t) = origin + p0 * (p1 - p0) of the near and far point of intersection. + *

+ * This method returns true for a line segment whose either end point lies inside the axis-aligned box. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectLineSegmentAab(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector2f) + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param minX + * the x coordinate of one corner of the axis-aligned box + * @param minY + * the y coordinate of one corner of the axis-aligned box + * @param minZ + * the z coordinate of one corner of the axis-aligned box + * @param maxX + * the x coordinate of the opposite corner of the axis-aligned box + * @param maxY + * the y coordinate of the opposite corner of the axis-aligned box + * @param maxZ + * the y coordinate of the opposite corner of the axis-aligned box + * @param result + * a vector which will hold the resulting values of the parameter + * t in the ray equation p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * iff the line segment intersects the axis-aligned box + * @return {@link #INSIDE} if the line segment lies completely inside of the axis-aligned box; or + * {@link #OUTSIDE} if the line segment lies completely outside of the axis-aligned box; or + * {@link #ONE_INTERSECTION} if one of the end points of the line segment lies inside of the axis-aligned box; or + * {@link #TWO_INTERSECTION} if the line segment intersects two sides of the axis-aligned box + * or lies on an edge or a side of the box + */ + public static int intersectLineSegmentAab(float p0X, float p0Y, float p0Z, float p1X, float p1Y, float p1Z, + float minX, float minY, float minZ, float maxX, float maxY, float maxZ, Vector2f result) { + float dirX = p1X - p0X, dirY = p1Y - p0Y, dirZ = p1Z - p0Z; + float invDirX = 1.0f / dirX, invDirY = 1.0f / dirY, invDirZ = 1.0f / dirZ; + float tNear, tFar, tymin, tymax, tzmin, tzmax; + if (invDirX >= 0.0f) { + tNear = (minX - p0X) * invDirX; + tFar = (maxX - p0X) * invDirX; + } else { + tNear = (maxX - p0X) * invDirX; + tFar = (minX - p0X) * invDirX; + } + if (invDirY >= 0.0f) { + tymin = (minY - p0Y) * invDirY; + tymax = (maxY - p0Y) * invDirY; + } else { + tymin = (maxY - p0Y) * invDirY; + tymax = (minY - p0Y) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return OUTSIDE; + if (invDirZ >= 0.0f) { + tzmin = (minZ - p0Z) * invDirZ; + tzmax = (maxZ - p0Z) * invDirZ; + } else { + tzmin = (maxZ - p0Z) * invDirZ; + tzmax = (minZ - p0Z) * invDirZ; + } + if (tNear > tzmax || tzmin > tFar) + return OUTSIDE; + tNear = tymin > tNear || Float.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Float.isNaN(tFar) ? tymax : tFar; + tNear = tzmin > tNear ? tzmin : tNear; + tFar = tzmax < tFar ? tzmax : tFar; + int type = OUTSIDE; + if (tNear <= tFar && tNear <= 1.0f && tFar >= 0.0f) { + if (tNear >= 0.0f && tFar > 1.0f) { + tFar = tNear; + type = ONE_INTERSECTION; + } else if (tNear < 0.0f && tFar <= 1.0f) { + tNear = tFar; + type = ONE_INTERSECTION; + } else if (tNear < 0.0f && tFar > 1.0f) { + type = INSIDE; + } else { + type = TWO_INTERSECTION; + } + result.x = tNear; + result.y = tFar; + } + return type; + } + + /** + * Determine whether the undirected line segment with the end points p0 and p1 + * intersects the axis-aligned box given as its minimum corner min and maximum corner max, + * and return the values of the parameter t in the ray equation p(t) = origin + p0 * (p1 - p0) of the near and far point of intersection. + *

+ * This method returns true for a line segment whose either end point lies inside the axis-aligned box. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectLineSegmentAab(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector2f) + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param result + * a vector which will hold the resulting values of the parameter + * t in the ray equation p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * iff the line segment intersects the axis-aligned box + * @return {@link #INSIDE} if the line segment lies completely inside of the axis-aligned box; or + * {@link #OUTSIDE} if the line segment lies completely outside of the axis-aligned box; or + * {@link #ONE_INTERSECTION} if one of the end points of the line segment lies inside of the axis-aligned box; or + * {@link #TWO_INTERSECTION} if the line segment intersects two sides of the axis-aligned box + * or lies on an edge or a side of the box + */ + public static int intersectLineSegmentAab(Vector3fc p0, Vector3fc p1, Vector3fc min, Vector3fc max, Vector2f result) { + return intersectLineSegmentAab(p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z(), min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), result); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the axis-aligned box given as its minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ). + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned box. + *

+ * If many boxes need to be tested against the same ray, then the {@link RayAabIntersection} class is likely more efficient. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #testRayAab(Vector3fc, Vector3fc, Vector3fc, Vector3fc) + * @see RayAabIntersection + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the y coordinate of the maximum corner of the axis-aligned box + * @return true if the given ray intersects the axis-aligned box; false otherwise + */ + public static boolean testRayAab(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + float invDirX = 1.0f / dirX, invDirY = 1.0f / dirY, invDirZ = 1.0f / dirZ; + float tNear, tFar, tymin, tymax, tzmin, tzmax; + if (invDirX >= 0.0f) { + tNear = (minX - originX) * invDirX; + tFar = (maxX - originX) * invDirX; + } else { + tNear = (maxX - originX) * invDirX; + tFar = (minX - originX) * invDirX; + } + if (invDirY >= 0.0f) { + tymin = (minY - originY) * invDirY; + tymax = (maxY - originY) * invDirY; + } else { + tymin = (maxY - originY) * invDirY; + tymax = (minY - originY) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return false; + if (invDirZ >= 0.0f) { + tzmin = (minZ - originZ) * invDirZ; + tzmax = (maxZ - originZ) * invDirZ; + } else { + tzmin = (maxZ - originZ) * invDirZ; + tzmax = (minZ - originZ) * invDirZ; + } + if (tNear > tzmax || tzmin > tFar) + return false; + tNear = tymin > tNear || Float.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Float.isNaN(tFar) ? tymax : tFar; + tNear = tzmin > tNear ? tzmin : tNear; + tFar = tzmax < tFar ? tzmax : tFar; + return tNear < tFar && tFar >= 0.0f; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the axis-aligned box specified as its minimum corner min and maximum corner max. + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned box. + *

+ * If many boxes need to be tested against the same ray, then the {@link RayAabIntersection} class is likely more efficient. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #testRayAab(float, float, float, float, float, float, float, float, float, float, float, float) + * @see RayAabIntersection + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @return true if the given ray intersects the axis-aligned box; false otherwise + */ + public static boolean testRayAab(Vector3fc origin, Vector3fc dir, Vector3fc min, Vector3fc max) { + return testRayAab(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), min.x(), min.y(), min.z(), max.x(), max.y(), max.z()); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the frontface of the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z). + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test implements backface culling, that is, it will return false when the triangle is in clockwise + * winding order assuming a right-handed coordinate system when seen along the ray's direction, even if the ray intersects the triangle. + * This is in compliance with how OpenGL handles backface culling with default frontface/backface settings. + * + * @see #testRayTriangleFront(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3fc, float) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return true if the given ray intersects the frontface of the triangle; false otherwise + */ + public static boolean testRayTriangleFront(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, + float epsilon) { + float edge1X = v1X - v0X; + float edge1Y = v1Y - v0Y; + float edge1Z = v1Z - v0Z; + float edge2X = v2X - v0X; + float edge2Y = v2Y - v0Y; + float edge2Z = v2Z - v0Z; + float pvecX = dirY * edge2Z - dirZ * edge2Y; + float pvecY = dirZ * edge2X - dirX * edge2Z; + float pvecZ = dirX * edge2Y - dirY * edge2X; + float det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det < epsilon) + return false; + float tvecX = originX - v0X; + float tvecY = originY - v0Y; + float tvecZ = originZ - v0Z; + float u = (tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ); + if (u < 0.0f || u > det) + return false; + float qvecX = tvecY * edge1Z - tvecZ * edge1Y; + float qvecY = tvecZ * edge1X - tvecX * edge1Z; + float qvecZ = tvecX * edge1Y - tvecY * edge1X; + float v = (dirX * qvecX + dirY * qvecY + dirZ * qvecZ); + if (v < 0.0f || u + v > det) + return false; + float invDet = 1.0f / det; + float t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t >= epsilon; + } + + /** + * Test whether the ray with the given origin and the given dir intersects the frontface of the triangle consisting of the three vertices + * v0, v1 and v2. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test implements backface culling, that is, it will return false when the triangle is in clockwise + * winding order assuming a right-handed coordinate system when seen along the ray's direction, even if the ray intersects the triangle. + * This is in compliance with how OpenGL handles backface culling with default frontface/backface settings. + * + * @see #testRayTriangleFront(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return true if the given ray intersects the frontface of the triangle; false otherwise + */ + public static boolean testRayTriangleFront(Vector3fc origin, Vector3fc dir, Vector3fc v0, Vector3fc v1, Vector3fc v2, float epsilon) { + return testRayTriangleFront(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Test whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z). + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test does not take into account the winding order of the triangle, so a ray will intersect a front-facing triangle as well as a back-facing triangle. + * + * @see #testRayTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3fc, float) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return true if the given ray intersects the frontface of the triangle; false otherwise + */ + public static boolean testRayTriangle(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, + float epsilon) { + float edge1X = v1X - v0X; + float edge1Y = v1Y - v0Y; + float edge1Z = v1Z - v0Z; + float edge2X = v2X - v0X; + float edge2Y = v2Y - v0Y; + float edge2Z = v2Z - v0Z; + float pvecX = dirY * edge2Z - dirZ * edge2Y; + float pvecY = dirZ * edge2X - dirX * edge2Z; + float pvecZ = dirX * edge2Y - dirY * edge2X; + float det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det > -epsilon && det < epsilon) + return false; + float tvecX = originX - v0X; + float tvecY = originY - v0Y; + float tvecZ = originZ - v0Z; + float invDet = 1.0f / det; + float u = (tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ) * invDet; + if (u < 0.0f || u > 1.0f) + return false; + float qvecX = tvecY * edge1Z - tvecZ * edge1Y; + float qvecY = tvecZ * edge1X - tvecX * edge1Z; + float qvecZ = tvecX * edge1Y - tvecY * edge1X; + float v = (dirX * qvecX + dirY * qvecY + dirZ * qvecZ) * invDet; + if (v < 0.0f || u + v > 1.0f) + return false; + float t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t >= epsilon; + } + + /** + * Test whether the ray with the given origin and the given dir intersects the frontface of the triangle consisting of the three vertices + * v0, v1 and v2. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test does not take into account the winding order of the triangle, so a ray will intersect a front-facing triangle as well as a back-facing triangle. + * + * @see #testRayTriangle(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return true if the given ray intersects the frontface of the triangle; false otherwise + */ + public static boolean testRayTriangle(Vector3fc origin, Vector3fc dir, Vector3fc v0, Vector3fc v1, Vector3fc v2, float epsilon) { + return testRayTriangle(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Determine whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the frontface of the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z) + * and return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test implements backface culling, that is, it will return false when the triangle is in clockwise + * winding order assuming a right-handed coordinate system when seen along the ray's direction, even if the ray intersects the triangle. + * This is in compliance with how OpenGL handles backface culling with default frontface/backface settings. + * + * @see #testRayTriangleFront(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3fc, float) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection + * if the ray intersects the frontface of the triangle; -1.0 otherwise + */ + public static float intersectRayTriangleFront(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, + float epsilon) { + float edge1X = v1X - v0X; + float edge1Y = v1Y - v0Y; + float edge1Z = v1Z - v0Z; + float edge2X = v2X - v0X; + float edge2Y = v2Y - v0Y; + float edge2Z = v2Z - v0Z; + float pvecX = dirY * edge2Z - dirZ * edge2Y; + float pvecY = dirZ * edge2X - dirX * edge2Z; + float pvecZ = dirX * edge2Y - dirY * edge2X; + float det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det <= epsilon) + return -1.0f; + float tvecX = originX - v0X; + float tvecY = originY - v0Y; + float tvecZ = originZ - v0Z; + float u = tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ; + if (u < 0.0f || u > det) + return -1.0f; + float qvecX = tvecY * edge1Z - tvecZ * edge1Y; + float qvecY = tvecZ * edge1X - tvecX * edge1Z; + float qvecZ = tvecX * edge1Y - tvecY * edge1X; + float v = dirX * qvecX + dirY * qvecY + dirZ * qvecZ; + if (v < 0.0f || u + v > det) + return -1.0f; + float invDet = 1.0f / det; + float t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t; + } + + /** + * Determine whether the ray with the given origin and the given dir intersects the frontface of the triangle consisting of the three vertices + * v0, v1 and v2 and return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test implements backface culling, that is, it will return false when the triangle is in clockwise + * winding order assuming a right-handed coordinate system when seen along the ray's direction, even if the ray intersects the triangle. + * This is in compliance with how OpenGL handles backface culling with default frontface/backface settings. + * + * @see #intersectRayTriangleFront(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection + * if the ray intersects the frontface of the triangle; -1.0 otherwise + */ + public static float intersectRayTriangleFront(Vector3fc origin, Vector3fc dir, Vector3fc v0, Vector3fc v1, Vector3fc v2, float epsilon) { + return intersectRayTriangleFront(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Determine whether the given ray with the origin (originX, originY, originZ) and direction (dirX, dirY, dirZ) + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z) + * and return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test does not take into account the winding order of the triangle, so a ray will intersect a front-facing triangle as well as a back-facing triangle. + * + * @see #testRayTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3fc, float) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param originZ + * the z coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param dirZ + * the z coordinate of the ray's direction + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection + * if the ray intersects the triangle; -1.0 otherwise + */ + public static float intersectRayTriangle(float originX, float originY, float originZ, float dirX, float dirY, float dirZ, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, + float epsilon) { + float edge1X = v1X - v0X; + float edge1Y = v1Y - v0Y; + float edge1Z = v1Z - v0Z; + float edge2X = v2X - v0X; + float edge2Y = v2Y - v0Y; + float edge2Z = v2Z - v0Z; + float pvecX = dirY * edge2Z - dirZ * edge2Y; + float pvecY = dirZ * edge2X - dirX * edge2Z; + float pvecZ = dirX * edge2Y - dirY * edge2X; + float det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det > -epsilon && det < epsilon) + return -1.0f; + float tvecX = originX - v0X; + float tvecY = originY - v0Y; + float tvecZ = originZ - v0Z; + float invDet = 1.0f / det; + float u = (tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ) * invDet; + if (u < 0.0f || u > 1.0f) + return -1.0f; + float qvecX = tvecY * edge1Z - tvecZ * edge1Y; + float qvecY = tvecZ * edge1X - tvecX * edge1Z; + float qvecZ = tvecX * edge1Y - tvecY * edge1X; + float v = (dirX * qvecX + dirY * qvecY + dirZ * qvecZ) * invDet; + if (v < 0.0f || u + v > 1.0f) + return -1.0f; + float t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t; + } + + /** + * Determine whether the ray with the given origin and the given dir intersects the triangle consisting of the three vertices + * v0, v1 and v2 and return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection. + *

+ * This is an implementation of the + * Fast, Minimum Storage Ray/Triangle Intersection method. + *

+ * This test does not take into account the winding order of the triangle, so a ray will intersect a front-facing triangle as well as a back-facing triangle. + * + * @see #intersectRayTriangle(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing rays that are almost parallel to the triangle + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the point of intersection + * if the ray intersects the triangle; -1.0 otherwise + */ + public static float intersectRayTriangle(Vector3fc origin, Vector3fc dir, Vector3fc v0, Vector3fc v1, Vector3fc v2, float epsilon) { + return intersectRayTriangle(origin.x(), origin.y(), origin.z(), dir.x(), dir.y(), dir.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Test whether the line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z), + * regardless of the winding order of the triangle or the direction of the line segment between its two end points. + *

+ * Reference: + * Fast, Minimum Storage Ray/Triangle Intersection + * + * @see #testLineSegmentTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3fc, float) + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing line segments that are almost parallel to the triangle + * @return true if the given line segment intersects the triangle; false otherwise + */ + public static boolean testLineSegmentTriangle(float p0X, float p0Y, float p0Z, float p1X, float p1Y, float p1Z, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, + float epsilon) { + float dirX = p1X - p0X; + float dirY = p1Y - p0Y; + float dirZ = p1Z - p0Z; + float t = intersectRayTriangle(p0X, p0Y, p0Z, dirX, dirY, dirZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z, epsilon); + return t >= 0.0f && t <= 1.0f; + } + + /** + * Test whether the line segment with the end points p0 and p1 + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z), + * regardless of the winding order of the triangle or the direction of the line segment between its two end points. + *

+ * Reference: + * Fast, Minimum Storage Ray/Triangle Intersection + * + * @see #testLineSegmentTriangle(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float) + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing line segments that are almost parallel to the triangle + * @return true if the given line segment intersects the triangle; false otherwise + */ + public static boolean testLineSegmentTriangle(Vector3fc p0, Vector3fc p1, Vector3fc v0, Vector3fc v1, Vector3fc v2, float epsilon) { + return testLineSegmentTriangle(p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon); + } + + /** + * Determine whether the line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z), + * regardless of the winding order of the triangle or the direction of the line segment between its two end points, + * and return the point of intersection. + *

+ * Reference: + * Fast, Minimum Storage Ray/Triangle Intersection + * + * @see #intersectLineSegmentTriangle(Vector3fc, Vector3fc, Vector3fc, Vector3fc, Vector3fc, float, Vector3f) + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param v0X + * the x coordinate of the first vertex + * @param v0Y + * the y coordinate of the first vertex + * @param v0Z + * the z coordinate of the first vertex + * @param v1X + * the x coordinate of the second vertex + * @param v1Y + * the y coordinate of the second vertex + * @param v1Z + * the z coordinate of the second vertex + * @param v2X + * the x coordinate of the third vertex + * @param v2Y + * the y coordinate of the third vertex + * @param v2Z + * the z coordinate of the third vertex + * @param epsilon + * a small epsilon when testing line segments that are almost parallel to the triangle + * @param intersectionPoint + * the point of intersection + * @return true if the given line segment intersects the triangle; false otherwise + */ + public static boolean intersectLineSegmentTriangle(float p0X, float p0Y, float p0Z, float p1X, float p1Y, float p1Z, + float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, float v2Z, + float epsilon, Vector3f intersectionPoint) { + float dirX = p1X - p0X; + float dirY = p1Y - p0Y; + float dirZ = p1Z - p0Z; + float t = intersectRayTriangle(p0X, p0Y, p0Z, dirX, dirY, dirZ, v0X, v0Y, v0Z, v1X, v1Y, v1Z, v2X, v2Y, v2Z, epsilon); + if (t >= 0.0f && t <= 1.0f) { + intersectionPoint.x = p0X + dirX * t; + intersectionPoint.y = p0Y + dirY * t; + intersectionPoint.z = p0Z + dirZ * t; + return true; + } + return false; + } + + /** + * Determine whether the line segment with the end points p0 and p1 + * intersects the triangle consisting of the three vertices (v0X, v0Y, v0Z), (v1X, v1Y, v1Z) and (v2X, v2Y, v2Z), + * regardless of the winding order of the triangle or the direction of the line segment between its two end points, + * and return the point of intersection. + *

+ * Reference: + * Fast, Minimum Storage Ray/Triangle Intersection + * + * @see #intersectLineSegmentTriangle(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, Vector3f) + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param v0 + * the position of the first vertex + * @param v1 + * the position of the second vertex + * @param v2 + * the position of the third vertex + * @param epsilon + * a small epsilon when testing line segments that are almost parallel to the triangle + * @param intersectionPoint + * the point of intersection + * @return true if the given line segment intersects the triangle; false otherwise + */ + public static boolean intersectLineSegmentTriangle(Vector3fc p0, Vector3fc p1, Vector3fc v0, Vector3fc v1, Vector3fc v2, float epsilon, Vector3f intersectionPoint) { + return intersectLineSegmentTriangle(p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z(), v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z(), v2.x(), v2.y(), v2.z(), epsilon, intersectionPoint); + } + + /** + * Determine whether the line segment with the end points (p0X, p0Y, p0Z) and (p1X, p1Y, p1Z) + * intersects the plane given as the general plane equation a*x + b*y + c*z + d = 0, + * and return the point of intersection. + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p0Z + * the z coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param p1Z + * the z coordinate of the line segment's second end point + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param intersectionPoint + * the point of intersection + * @return true if the given line segment intersects the plane; false otherwise + */ + public static boolean intersectLineSegmentPlane(float p0X, float p0Y, float p0Z, float p1X, float p1Y, float p1Z, + float a, float b, float c, float d, Vector3f intersectionPoint) { + float dirX = p1X - p0X; + float dirY = p1Y - p0Y; + float dirZ = p1Z - p0Z; + float denom = a * dirX + b * dirY + c * dirZ; + float t = -(a * p0X + b * p0Y + c * p0Z + d) / denom; + if (t >= 0.0f && t <= 1.0f) { + intersectionPoint.x = p0X + t * dirX; + intersectionPoint.y = p0Y + t * dirY; + intersectionPoint.z = p0Z + t * dirZ; + return true; + } + return false; + } + + /** + * Test whether the line with the general line equation a*x + b*y + c = 0 intersects the circle with center + * (centerX, centerY) and radius. + *

+ * Reference: http://math.stackexchange.com + * + * @param a + * the x factor in the line equation + * @param b + * the y factor in the line equation + * @param c + * the constant in the line equation + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radius + * the radius of the circle + * @return true iff the line intersects the circle; false otherwise + */ + public static boolean testLineCircle(float a, float b, float c, float centerX, float centerY, float radius) { + float denom = (float) Math.sqrt(a * a + b * b); + float dist = (a * centerX + b * centerY + c) / denom; + return -radius <= dist && dist <= radius; + } + + /** + * Test whether the line with the general line equation a*x + b*y + c = 0 intersects the circle with center + * (centerX, centerY) and radius, and store the center of the line segment of + * intersection in the (x, y) components of the supplied vector and the half-length of that line segment in the z component. + *

+ * Reference: http://math.stackexchange.com + * + * @param a + * the x factor in the line equation + * @param b + * the y factor in the line equation + * @param c + * the constant in the line equation + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radius + * the radius of the circle + * @param intersectionCenterAndHL + * will hold the center of the line segment of intersection in the (x, y) components and the half-length in the z component + * @return true iff the line intersects the circle; false otherwise + */ + public static boolean intersectLineCircle(float a, float b, float c, float centerX, float centerY, float radius, Vector3f intersectionCenterAndHL) { + float invDenom = Math.invsqrt(a * a + b * b); + float dist = (a * centerX + b * centerY + c) * invDenom; + if (-radius <= dist && dist <= radius) { + intersectionCenterAndHL.x = centerX + dist * a * invDenom; + intersectionCenterAndHL.y = centerY + dist * b * invDenom; + intersectionCenterAndHL.z = (float) Math.sqrt(radius * radius - dist * dist); + return true; + } + return false; + } + + /** + * Test whether the line defined by the two points (x0, y0) and (x1, y1) intersects the circle with center + * (centerX, centerY) and radius, and store the center of the line segment of + * intersection in the (x, y) components of the supplied vector and the half-length of that line segment in the z component. + *

+ * Reference: http://math.stackexchange.com + * + * @param x0 + * the x coordinate of the first point on the line + * @param y0 + * the y coordinate of the first point on the line + * @param x1 + * the x coordinate of the second point on the line + * @param y1 + * the y coordinate of the second point on the line + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radius + * the radius of the circle + * @param intersectionCenterAndHL + * will hold the center of the line segment of intersection in the (x, y) components and the half-length in the z component + * @return true iff the line intersects the circle; false otherwise + */ + public static boolean intersectLineCircle(float x0, float y0, float x1, float y1, float centerX, float centerY, float radius, Vector3f intersectionCenterAndHL) { + // Build general line equation from two points and use the other method + return intersectLineCircle(y0 - y1, x1 - x0, (x0 - x1) * y0 + (y1 - y0) * x0, centerX, centerY, radius, intersectionCenterAndHL); + } + + /** + * Test whether the axis-aligned rectangle with minimum corner (minX, minY) and maximum corner (maxX, maxY) + * intersects the line with the general equation a*x + b*y + c = 0. + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param a + * the x factor in the line equation + * @param b + * the y factor in the line equation + * @param c + * the constant in the plane equation + * @return true iff the axis-aligned rectangle intersects the line; false otherwise + */ + public static boolean testAarLine(float minX, float minY, float maxX, float maxY, float a, float b, float c) { + float pX, pY, nX, nY; + if (a > 0.0f) { + pX = maxX; + nX = minX; + } else { + pX = minX; + nX = maxX; + } + if (b > 0.0f) { + pY = maxY; + nY = minY; + } else { + pY = minY; + nY = maxY; + } + float distN = c + a * nX + b * nY; + float distP = c + a * pX + b * pY; + return distN <= 0.0f && distP >= 0.0f; + } + + /** + * Test whether the axis-aligned rectangle with minimum corner min and maximum corner max + * intersects the line with the general equation a*x + b*y + c = 0. + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @param a + * the x factor in the line equation + * @param b + * the y factor in the line equation + * @param c + * the constant in the line equation + * @return true iff the axis-aligned rectangle intersects the line; false otherwise + */ + public static boolean testAarLine(Vector2fc min, Vector2fc max, float a, float b, float c) { + return testAarLine(min.x(), min.y(), max.x(), max.y(), a, b, c); + } + + /** + * Test whether the axis-aligned rectangle with minimum corner (minX, minY) and maximum corner (maxX, maxY) + * intersects the line defined by the two points (x0, y0) and (x1, y1). + *

+ * Reference: http://www.lighthouse3d.com ("Geometric Approach - Testing Boxes II") + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param x0 + * the x coordinate of the first point on the line + * @param y0 + * the y coordinate of the first point on the line + * @param x1 + * the x coordinate of the second point on the line + * @param y1 + * the y coordinate of the second point on the line + * @return true iff the axis-aligned rectangle intersects the line; false otherwise + */ + public static boolean testAarLine(float minX, float minY, float maxX, float maxY, float x0, float y0, float x1, float y1) { + float a = y0 - y1; + float b = x1 - x0; + float c = -b * y0 - a * x0; + return testAarLine(minX, minY, maxX, maxY, a, b, c); + } + + /** + * Test whether the axis-aligned rectangle with minimum corner (minXA, minYA) and maximum corner (maxXA, maxYA) + * intersects the axis-aligned rectangle with minimum corner (minXB, minYB) and maximum corner (maxXB, maxYB). + * + * @param minXA + * the x coordinate of the minimum corner of the first axis-aligned rectangle + * @param minYA + * the y coordinate of the minimum corner of the first axis-aligned rectangle + * @param maxXA + * the x coordinate of the maximum corner of the first axis-aligned rectangle + * @param maxYA + * the y coordinate of the maximum corner of the first axis-aligned rectangle + * @param minXB + * the x coordinate of the minimum corner of the second axis-aligned rectangle + * @param minYB + * the y coordinate of the minimum corner of the second axis-aligned rectangle + * @param maxXB + * the x coordinate of the maximum corner of the second axis-aligned rectangle + * @param maxYB + * the y coordinate of the maximum corner of the second axis-aligned rectangle + * @return true iff both axis-aligned rectangles intersect; false otherwise + */ + public static boolean testAarAar(float minXA, float minYA, float maxXA, float maxYA, float minXB, float minYB, float maxXB, float maxYB) { + return maxXA >= minXB && maxYA >= minYB && minXA <= maxXB && minYA <= maxYB; + } + + /** + * Test whether the axis-aligned rectangle with minimum corner minA and maximum corner maxA + * intersects the axis-aligned rectangle with minimum corner minB and maximum corner maxB. + * + * @param minA + * the minimum corner of the first axis-aligned rectangle + * @param maxA + * the maximum corner of the first axis-aligned rectangle + * @param minB + * the minimum corner of the second axis-aligned rectangle + * @param maxB + * the maximum corner of the second axis-aligned rectangle + * @return true iff both axis-aligned rectangles intersect; false otherwise + */ + public static boolean testAarAar(Vector2fc minA, Vector2fc maxA, Vector2fc minB, Vector2fc maxB) { + return testAarAar(minA.x(), minA.y(), maxA.x(), maxA.y(), minB.x(), minB.y(), maxB.x(), maxB.y()); + } + + /** + * Test whether a given circle with center (aX, aY) and radius aR and travelled distance vector (maX, maY) + * intersects a given static circle with center (bX, bY) and radius bR. + *

+ * Note that the case of two moving circles can always be reduced to this case by expressing the moved distance of one of the circles relative + * to the other. + *

+ * Reference: https://www.gamasutra.com + * + * @param aX + * the x coordinate of the first circle's center + * @param aY + * the y coordinate of the first circle's center + * @param maX + * the x coordinate of the first circle's travelled distance vector + * @param maY + * the y coordinate of the first circle's travelled distance vector + * @param aR + * the radius of the first circle + * @param bX + * the x coordinate of the second circle's center + * @param bY + * the y coordinate of the second circle's center + * @param bR + * the radius of the second circle + * @return true if both circle intersect; false otherwise + */ + public static boolean testMovingCircleCircle(float aX, float aY, float maX, float maY, float aR, float bX, float bY, float bR) { + float aRbR = aR + bR; + float dist = (float) Math.sqrt((aX - bX) * (aX - bX) + (aY - bY) * (aY - bY)) - aRbR; + float mLen = (float) Math.sqrt(maX * maX + maY * maY); + if (mLen < dist) + return false; + float invMLen = 1.0f / mLen; + float nX = maX * invMLen; + float nY = maY * invMLen; + float cX = bX - aX; + float cY = bY - aY; + float nDotC = nX * cX + nY * cY; + if (nDotC <= 0.0f) + return false; + float cLen = (float) Math.sqrt(cX * cX + cY * cY); + float cLenNdotC = cLen * cLen - nDotC * nDotC; + float aRbR2 = aRbR * aRbR; + if (cLenNdotC >= aRbR2) + return false; + float t = aRbR2 - cLenNdotC; + if (t < 0.0f) + return false; + float distance = nDotC - (float) Math.sqrt(t); + float mag = mLen; + if (mag < distance) + return false; + return true; + } + + /** + * Test whether a given circle with center centerA and radius aR and travelled distance vector moveA + * intersects a given static circle with center centerB and radius bR. + *

+ * Note that the case of two moving circles can always be reduced to this case by expressing the moved distance of one of the circles relative + * to the other. + *

+ * Reference: https://www.gamasutra.com + * + * @param centerA + * the coordinates of the first circle's center + * @param moveA + * the coordinates of the first circle's travelled distance vector + * @param aR + * the radius of the first circle + * @param centerB + * the coordinates of the second circle's center + * @param bR + * the radius of the second circle + * @return true if both circle intersect; false otherwise + */ + public static boolean testMovingCircleCircle(Vector2f centerA, Vector2f moveA, float aR, Vector2f centerB, float bR) { + return testMovingCircleCircle(centerA.x, centerA.y, moveA.x, moveA.y, aR, centerB.x, centerB.y, bR); + } + + /** + * Test whether the one circle with center (aX, aY) and square radius radiusSquaredA intersects the other + * circle with center (bX, bY) and square radius radiusSquaredB, and store the center of the line segment of + * intersection in the (x, y) components of the supplied vector and the half-length of that line segment in the z component. + *

+ * This method returns false when one circle contains the other circle. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param aX + * the x coordinate of the first circle's center + * @param aY + * the y coordinate of the first circle's center + * @param radiusSquaredA + * the square of the first circle's radius + * @param bX + * the x coordinate of the second circle's center + * @param bY + * the y coordinate of the second circle's center + * @param radiusSquaredB + * the square of the second circle's radius + * @param intersectionCenterAndHL + * will hold the center of the circle of intersection in the (x, y, z) components and the radius in the w component + * @return true iff both circles intersect; false otherwise + */ + public static boolean intersectCircleCircle(float aX, float aY, float radiusSquaredA, float bX, float bY, float radiusSquaredB, Vector3f intersectionCenterAndHL) { + float dX = bX - aX, dY = bY - aY; + float distSquared = dX * dX + dY * dY; + float h = 0.5f + (radiusSquaredA - radiusSquaredB) / distSquared; + float r_i = (float) Math.sqrt(radiusSquaredA - h * h * distSquared); + if (r_i >= 0.0f) { + intersectionCenterAndHL.x = aX + h * dX; + intersectionCenterAndHL.y = aY + h * dY; + intersectionCenterAndHL.z = r_i; + return true; + } + return false; + } + + /** + * Test whether the one circle with center centerA and square radius radiusSquaredA intersects the other + * circle with center centerB and square radius radiusSquaredB, and store the center of the line segment of + * intersection in the (x, y) components of the supplied vector and the half-length of that line segment in the z component. + *

+ * This method returns false when one circle contains the other circle. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param centerA + * the first circle's center + * @param radiusSquaredA + * the square of the first circle's radius + * @param centerB + * the second circle's center + * @param radiusSquaredB + * the square of the second circle's radius + * @param intersectionCenterAndHL + * will hold the center of the line segment of intersection in the (x, y) components and the half-length in the z component + * @return true iff both circles intersect; false otherwise + */ + public static boolean intersectCircleCircle(Vector2fc centerA, float radiusSquaredA, Vector2fc centerB, float radiusSquaredB, Vector3f intersectionCenterAndHL) { + return intersectCircleCircle(centerA.x(), centerA.y(), radiusSquaredA, centerB.x(), centerB.y(), radiusSquaredB, intersectionCenterAndHL); + } + + /** + * Test whether the one circle with center (aX, aY) and radius rA intersects the other circle with center (bX, bY) and radius rB. + *

+ * This method returns true when one circle contains the other circle. + *

+ * Reference: http://math.stackexchange.com/ + * + * @param aX + * the x coordinate of the first circle's center + * @param aY + * the y coordinate of the first circle's center + * @param rA + * the square of the first circle's radius + * @param bX + * the x coordinate of the second circle's center + * @param bY + * the y coordinate of the second circle's center + * @param rB + * the square of the second circle's radius + * @return true iff both circles intersect; false otherwise + */ + public static boolean testCircleCircle(float aX, float aY, float rA, float bX, float bY, float rB) { + float d = (aX - bX) * (aX - bX) + (aY - bY) * (aY - bY); + return d <= (rA + rB) * (rA + rB); + } + + /** + * Test whether the one circle with center centerA and square radius radiusSquaredA intersects the other + * circle with center centerB and square radius radiusSquaredB. + *

+ * This method returns true when one circle contains the other circle. + *

+ * Reference: http://gamedev.stackexchange.com + * + * @param centerA + * the first circle's center + * @param radiusSquaredA + * the square of the first circle's radius + * @param centerB + * the second circle's center + * @param radiusSquaredB + * the square of the second circle's radius + * @return true iff both circles intersect; false otherwise + */ + public static boolean testCircleCircle(Vector2fc centerA, float radiusSquaredA, Vector2fc centerB, float radiusSquaredB) { + return testCircleCircle(centerA.x(), centerA.y(), radiusSquaredA, centerB.x(), centerB.y(), radiusSquaredB); + } + + /** + * Determine the signed distance of the given point (pointX, pointY) to the line specified via its general plane equation + * a*x + b*y + c = 0. + *

+ * Reference: http://mathworld.wolfram.com + * + * @param pointX + * the x coordinate of the point + * @param pointY + * the y coordinate of the point + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the constant in the plane equation + * @return the distance between the point and the line + */ + public static float distancePointLine(float pointX, float pointY, float a, float b, float c) { + float denom = (float) Math.sqrt(a * a + b * b); + return (a * pointX + b * pointY + c) / denom; + } + + /** + * Determine the signed distance of the given point (pointX, pointY) to the line defined by the two points (x0, y0) and (x1, y1). + *

+ * Reference: http://mathworld.wolfram.com + * + * @param pointX + * the x coordinate of the point + * @param pointY + * the y coordinate of the point + * @param x0 + * the x coordinate of the first point on the line + * @param y0 + * the y coordinate of the first point on the line + * @param x1 + * the x coordinate of the second point on the line + * @param y1 + * the y coordinate of the second point on the line + * @return the distance between the point and the line + */ + public static float distancePointLine(float pointX, float pointY, float x0, float y0, float x1, float y1) { + float dx = x1 - x0; + float dy = y1 - y0; + float denom = (float) Math.sqrt(dx * dx + dy * dy); + return (dx * (y0 - pointY) - (x0 - pointX) * dy) / denom; + } + + /** + * Compute the distance of the given point (pX, pY, pZ) to the line defined by the two points (x0, y0, z0) and (x1, y1, z1). + *

+ * Reference: http://mathworld.wolfram.com + * + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param pZ + * the z coordinate of the point + * @param x0 + * the x coordinate of the first point on the line + * @param y0 + * the y coordinate of the first point on the line + * @param z0 + * the z coordinate of the first point on the line + * @param x1 + * the x coordinate of the second point on the line + * @param y1 + * the y coordinate of the second point on the line + * @param z1 + * the z coordinate of the second point on the line + * @return the distance between the point and the line + */ + public static float distancePointLine(float pX, float pY, float pZ, + float x0, float y0, float z0, float x1, float y1, float z1) { + float d21x = x1 - x0, d21y = y1 - y0, d21z = z1 - z0; + float d10x = x0 - pX, d10y = y0 - pY, d10z = z0 - pZ; + float cx = d21y * d10z - d21z * d10y, cy = d21z * d10x - d21x * d10z, cz = d21x * d10y - d21y * d10x; + return (float) Math.sqrt((cx*cx + cy*cy + cz*cz) / (d21x*d21x + d21y*d21y + d21z*d21z)); + } + + /** + * Test whether the ray with given origin (originX, originY) and direction (dirX, dirY) intersects the line + * containing the given point (pointX, pointY) and having the normal (normalX, normalY), and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the line, because it is either parallel to the line or its direction points + * away from the line or the ray's origin is on the negative side of the line (i.e. the line's normal points away from the ray's origin). + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param pointX + * the x coordinate of a point on the line + * @param pointY + * the y coordinate of a point on the line + * @param normalX + * the x coordinate of the line's normal + * @param normalY + * the y coordinate of the line's normal + * @param epsilon + * some small epsilon for when the ray is parallel to the line + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the line; -1.0 otherwise + */ + public static float intersectRayLine(float originX, float originY, float dirX, float dirY, float pointX, float pointY, float normalX, float normalY, float epsilon) { + float denom = normalX * dirX + normalY * dirY; + if (denom < epsilon) { + float t = ((pointX - originX) * normalX + (pointY - originY) * normalY) / denom; + if (t >= 0.0f) + return t; + } + return -1.0f; + } + + /** + * Test whether the ray with given origin and direction dir intersects the line + * containing the given point and having the given normal, and return the + * value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point. + *

+ * This method returns -1.0 if the ray does not intersect the line, because it is either parallel to the line or its direction points + * away from the line or the ray's origin is on the negative side of the line (i.e. the line's normal points away from the ray's origin). + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param point + * a point on the line + * @param normal + * the line's normal + * @param epsilon + * some small epsilon for when the ray is parallel to the line + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the line; -1.0 otherwise + */ + public static float intersectRayLine(Vector2fc origin, Vector2fc dir, Vector2fc point, Vector2fc normal, float epsilon) { + return intersectRayLine(origin.x(), origin.y(), dir.x(), dir.y(), point.x(), point.y(), normal.x(), normal.y(), epsilon); + } + + /** + * Determine whether the ray with given origin (originX, originY) and direction (dirX, dirY) intersects the undirected line segment + * given by the two end points (aX, bY) and (bX, bY), and return the value of the parameter t in the ray equation + * p(t) = origin + t * dir of the intersection point, if any. + *

+ * This method returns -1.0 if the ray does not intersect the line segment. + * + * @see #intersectRayLineSegment(Vector2fc, Vector2fc, Vector2fc, Vector2fc) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param aX + * the x coordinate of the line segment's first end point + * @param aY + * the y coordinate of the line segment's first end point + * @param bX + * the x coordinate of the line segment's second end point + * @param bY + * the y coordinate of the line segment's second end point + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the line segment; -1.0 otherwise + */ + public static float intersectRayLineSegment(float originX, float originY, float dirX, float dirY, float aX, float aY, float bX, float bY) { + float v1X = originX - aX; + float v1Y = originY - aY; + float v2X = bX - aX; + float v2Y = bY - aY; + float invV23 = 1.0f / (v2Y * dirX - v2X * dirY); + float t1 = (v2X * v1Y - v2Y * v1X) * invV23; + float t2 = (v1Y * dirX - v1X * dirY) * invV23; + if (t1 >= 0.0f && t2 >= 0.0f && t2 <= 1.0f) + return t1; + return -1.0f; + } + + /** + * Determine whether the ray with given origin and direction dir intersects the undirected line segment + * given by the two end points a and b, and return the value of the parameter t in the ray equation + * p(t) = origin + t * dir of the intersection point, if any. + *

+ * This method returns -1.0 if the ray does not intersect the line segment. + * + * @see #intersectRayLineSegment(float, float, float, float, float, float, float, float) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param a + * the line segment's first end point + * @param b + * the line segment's second end point + * @return the value of the parameter t in the ray equation p(t) = origin + t * dir of the intersection point, if the ray + * intersects the line segment; -1.0 otherwise + */ + public static float intersectRayLineSegment(Vector2fc origin, Vector2fc dir, Vector2fc a, Vector2fc b) { + return intersectRayLineSegment(origin.x(), origin.y(), dir.x(), dir.y(), a.x(), a.y(), b.x(), b.y()); + } + + /** + * Test whether the axis-aligned rectangle with minimum corner (minX, minY) and maximum corner (maxX, maxY) + * intersects the circle with the given center (centerX, centerY) and square radius radiusSquared. + *

+ * Reference: http://stackoverflow.com + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the square of the circle's radius + * @return true iff the axis-aligned rectangle intersects the circle; false otherwise + */ + public static boolean testAarCircle(float minX, float minY, float maxX, float maxY, float centerX, float centerY, float radiusSquared) { + float radius2 = radiusSquared; + if (centerX < minX) { + float d = (centerX - minX); + radius2 -= d * d; + } else if (centerX > maxX) { + float d = (centerX - maxX); + radius2 -= d * d; + } + if (centerY < minY) { + float d = (centerY - minY); + radius2 -= d * d; + } else if (centerY > maxY) { + float d = (centerY - maxY); + radius2 -= d * d; + } + return radius2 >= 0.0f; + } + + /** + * Test whether the axis-aligned rectangle with minimum corner min and maximum corner max + * intersects the circle with the given center and square radius radiusSquared. + *

+ * Reference: http://stackoverflow.com + * + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @param center + * the circle's center + * @param radiusSquared + * the squared of the circle's radius + * @return true iff the axis-aligned rectangle intersects the circle; false otherwise + */ + public static boolean testAarCircle(Vector2fc min, Vector2fc max, Vector2fc center, float radiusSquared) { + return testAarCircle(min.x(), min.y(), max.x(), max.y(), center.x(), center.y(), radiusSquared); + } + + /** + * Determine the closest point on the triangle with the given vertices (v0X, v0Y), (v1X, v1Y), (v2X, v2Y) + * between that triangle and the given point (pX, pY) and store that point into the given result. + *

+ * Additionally, this method returns whether the closest point is a vertex ({@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}) + * of the triangle, lies on an edge ({@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20}) + * or on the {@link #POINT_ON_TRIANGLE_FACE face} of the triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.5 "Closest Point on Triangle to Point" + * + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param result + * will hold the closest point + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} + */ + public static int findClosestPointOnTriangle(float v0X, float v0Y, float v1X, float v1Y, float v2X, float v2Y, float pX, float pY, Vector2f result) { + float abX = v1X - v0X, abY = v1Y - v0Y; + float acX = v2X - v0X, acY = v2Y - v0Y; + float apX = pX - v0X, apY = pY - v0Y; + float d1 = abX * apX + abY * apY; + float d2 = acX * apX + acY * apY; + if (d1 <= 0.0f && d2 <= 0.0f) { + result.x = v0X; + result.y = v0Y; + return POINT_ON_TRIANGLE_VERTEX_0; + } + float bpX = pX - v1X, bpY = pY - v1Y; + float d3 = abX * bpX + abY * bpY; + float d4 = acX * bpX + acY * bpY; + if (d3 >= 0.0f && d4 <= d3) { + result.x = v1X; + result.y = v1Y; + return POINT_ON_TRIANGLE_VERTEX_1; + } + float vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) { + float v = d1 / (d1 - d3); + result.x = v0X + v * abX; + result.y = v0Y + v * abY; + return POINT_ON_TRIANGLE_EDGE_01; + } + float cpX = pX - v2X, cpY = pY - v2Y; + float d5 = abX * cpX + abY * cpY; + float d6 = acX * cpX + acY * cpY; + if (d6 >= 0.0f && d5 <= d6) { + result.x = v2X; + result.y = v2Y; + return POINT_ON_TRIANGLE_VERTEX_2; + } + float vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) { + float w = d2 / (d2 - d6); + result.x = v0X + w * acX; + result.y = v0Y + w * acY; + return POINT_ON_TRIANGLE_EDGE_20; + } + float va = d3 * d6 - d5 * d4; + if (va <= 0.0f && d4 - d3 >= 0.0f && d5 - d6 >= 0.0f) { + float w = (d4 - d3) / (d4 - d3 + d5 - d6); + result.x = v1X + w * (v2X - v1X); + result.y = v1Y + w * (v2Y - v1Y); + return POINT_ON_TRIANGLE_EDGE_12; + } + float denom = 1.0f / (va + vb + vc); + float v = vb * denom; + float w = vc * denom; + result.x = v0X + abX * v + acX * w; + result.y = v0Y + abY * v + acY * w; + return POINT_ON_TRIANGLE_FACE; + } + + /** + * Determine the closest point on the triangle with the vertices v0, v1, v2 + * between that triangle and the given point p and store that point into the given result. + *

+ * Additionally, this method returns whether the closest point is a vertex ({@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}) + * of the triangle, lies on an edge ({@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20}) + * or on the {@link #POINT_ON_TRIANGLE_FACE face} of the triangle. + *

+ * Reference: Book "Real-Time Collision Detection" chapter 5.1.5 "Closest Point on Triangle to Point" + * + * @param v0 + * the first vertex of the triangle + * @param v1 + * the second vertex of the triangle + * @param v2 + * the third vertex of the triangle + * @param p + * the point + * @param result + * will hold the closest point + * @return one of {@link #POINT_ON_TRIANGLE_VERTEX_0}, {@link #POINT_ON_TRIANGLE_VERTEX_1}, {@link #POINT_ON_TRIANGLE_VERTEX_2}, + * {@link #POINT_ON_TRIANGLE_EDGE_01}, {@link #POINT_ON_TRIANGLE_EDGE_12}, {@link #POINT_ON_TRIANGLE_EDGE_20} or + * {@link #POINT_ON_TRIANGLE_FACE} + */ + public static int findClosestPointOnTriangle(Vector2fc v0, Vector2fc v1, Vector2fc v2, Vector2fc p, Vector2f result) { + return findClosestPointOnTriangle(v0.x(), v0.y(), v1.x(), v1.y(), v2.x(), v2.y(), p.x(), p.y(), result); + } + + /** + * Test whether the given ray with the origin (originX, originY) and direction (dirX, dirY) + * intersects the given circle with center (centerX, centerY) and square radius radiusSquared, + * and store the values of the parameter t in the ray equation p(t) = origin + t * dir for both points (near + * and far) of intersections into the given result vector. + *

+ * This method returns true for a ray whose origin lies inside the circle. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the circle radius squared + * @param result + * a vector that will contain the values of the parameter t in the ray equation + * p(t) = origin + t * dir for both points (near, far) of intersections with the circle + * @return true if the ray intersects the circle; false otherwise + */ + public static boolean intersectRayCircle(float originX, float originY, float dirX, float dirY, + float centerX, float centerY, float radiusSquared, Vector2f result) { + float Lx = centerX - originX; + float Ly = centerY - originY; + float tca = Lx * dirX + Ly * dirY; + float d2 = Lx * Lx + Ly * Ly - tca * tca; + if (d2 > radiusSquared) + return false; + float thc = (float) Math.sqrt(radiusSquared - d2); + float t0 = tca - thc; + float t1 = tca + thc; + if (t0 < t1 && t1 >= 0.0f) { + result.x = t0; + result.y = t1; + return true; + } + return false; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the circle with the given center and square radius radiusSquared, + * and store the values of the parameter t in the ray equation p(t) = origin + t * dir for both points (near + * and far) of intersections into the given result vector. + *

+ * This method returns true for a ray whose origin lies inside the circle. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param center + * the circle's center + * @param radiusSquared + * the circle radius squared + * @param result + * a vector that will contain the values of the parameter t in the ray equation + * p(t) = origin + t * dir for both points (near, far) of intersections with the circle + * @return true if the ray intersects the circle; false otherwise + */ + public static boolean intersectRayCircle(Vector2fc origin, Vector2fc dir, Vector2fc center, float radiusSquared, Vector2f result) { + return intersectRayCircle(origin.x(), origin.y(), dir.x(), dir.y(), center.x(), center.y(), radiusSquared, result); + } + + /** + * Test whether the given ray with the origin (originX, originY) and direction (dirX, dirY) + * intersects the given circle with center (centerX, centerY) and square radius radiusSquared. + *

+ * This method returns true for a ray whose origin lies inside the circle. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the circle radius squared + * @return true if the ray intersects the circle; false otherwise + */ + public static boolean testRayCircle(float originX, float originY, float dirX, float dirY, + float centerX, float centerY, float radiusSquared) { + float Lx = centerX - originX; + float Ly = centerY - originY; + float tca = Lx * dirX + Ly * dirY; + float d2 = Lx * Lx + Ly * Ly - tca * tca; + if (d2 > radiusSquared) + return false; + float thc = (float) Math.sqrt(radiusSquared - d2); + float t0 = tca - thc; + float t1 = tca + thc; + return t0 < t1 && t1 >= 0.0f; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the circle with the given center and square radius. + *

+ * This method returns true for a ray whose origin lies inside the circle. + *

+ * Reference: http://www.scratchapixel.com/ + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param center + * the circle's center + * @param radiusSquared + * the circle radius squared + * @return true if the ray intersects the circle; false otherwise + */ + public static boolean testRayCircle(Vector2fc origin, Vector2fc dir, Vector2fc center, float radiusSquared) { + return testRayCircle(origin.x(), origin.y(), dir.x(), dir.y(), center.x(), center.y(), radiusSquared); + } + + /** + * Determine whether the given ray with the origin (originX, originY) and direction (dirX, dirY) + * intersects the axis-aligned rectangle given as its minimum corner (minX, minY) and maximum corner (maxX, maxY), + * and return the values of the parameter t in the ray equation p(t) = origin + t * dir of the near and far point of intersection + * as well as the side of the axis-aligned rectangle the ray intersects. + *

+ * This method also detects an intersection for a ray whose origin lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectRayAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param result + * a vector which will hold the values of the parameter t in the ray equation + * p(t) = origin + t * dir of the near and far point of intersection + * @return the side on which the near intersection occurred as one of + * {@link #AAR_SIDE_MINX}, {@link #AAR_SIDE_MINY}, {@link #AAR_SIDE_MAXX} or {@link #AAR_SIDE_MAXY}; + * or -1 if the ray does not intersect the axis-aligned rectangle; + */ + public static int intersectRayAar(float originX, float originY, float dirX, float dirY, + float minX, float minY, float maxX, float maxY, Vector2f result) { + float invDirX = 1.0f / dirX, invDirY = 1.0f / dirY; + float tNear, tFar, tymin, tymax; + if (invDirX >= 0.0f) { + tNear = (minX - originX) * invDirX; + tFar = (maxX - originX) * invDirX; + } else { + tNear = (maxX - originX) * invDirX; + tFar = (minX - originX) * invDirX; + } + if (invDirY >= 0.0f) { + tymin = (minY - originY) * invDirY; + tymax = (maxY - originY) * invDirY; + } else { + tymin = (maxY - originY) * invDirY; + tymax = (minY - originY) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return OUTSIDE; + tNear = tymin > tNear || Float.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Float.isNaN(tFar) ? tymax : tFar; + int side = -1; // no intersection side + if (tNear <= tFar && tFar >= 0.0f) { + float px = originX + tNear * dirX; + float py = originY + tNear * dirY; + result.x = tNear; + result.y = tFar; + float daX = Math.abs(px - minX); + float daY = Math.abs(py - minY); + float dbX = Math.abs(px - maxX); + float dbY = Math.abs(py - maxY); + side = 0; // min x coordinate + float min = daX; + if (daY < min) { + min = daY; + side = 1; // min y coordinate + } + if (dbX < min) { + min = dbX; + side = 2; // max xcoordinate + } + if (dbY < min) + side = 3; // max y coordinate + } + return side; + } + + /** + * Determine whether the given ray with the given origin and direction dir + * intersects the axis-aligned rectangle given as its minimum corner min and maximum corner max, + * and return the values of the parameter t in the ray equation p(t) = origin + t * dir of the near and far point of intersection + * as well as the side of the axis-aligned rectangle the ray intersects. + *

+ * This method also detects an intersection for a ray whose origin lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectRayAar(float, float, float, float, float, float, float, float, Vector2f) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @param result + * a vector which will hold the values of the parameter t in the ray equation + * p(t) = origin + t * dir of the near and far point of intersection + * @return the side on which the near intersection occurred as one of + * {@link #AAR_SIDE_MINX}, {@link #AAR_SIDE_MINY}, {@link #AAR_SIDE_MAXX} or {@link #AAR_SIDE_MAXY}; + * or -1 if the ray does not intersect the axis-aligned rectangle; + */ + public static int intersectRayAar(Vector2fc origin, Vector2fc dir, Vector2fc min, Vector2fc max, Vector2f result) { + return intersectRayAar(origin.x(), origin.y(), dir.x(), dir.y(), min.x(), min.y(), max.x(), max.y(), result); + } + + /** + * Determine whether the undirected line segment with the end points (p0X, p0Y) and (p1X, p1Y) + * intersects the axis-aligned rectangle given as its minimum corner (minX, minY) and maximum corner (maxX, maxY), + * and store the values of the parameter t in the ray equation p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * into result. + *

+ * This method also detects an intersection of a line segment whose either end point lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #intersectLineSegmentAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc, Vector2f) + * + * @param p0X + * the x coordinate of the line segment's first end point + * @param p0Y + * the y coordinate of the line segment's first end point + * @param p1X + * the x coordinate of the line segment's second end point + * @param p1Y + * the y coordinate of the line segment's second end point + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @param result + * a vector which will hold the values of the parameter t in the ray equation + * p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * @return {@link #INSIDE} if the line segment lies completely inside of the axis-aligned rectangle; or + * {@link #OUTSIDE} if the line segment lies completely outside of the axis-aligned rectangle; or + * {@link #ONE_INTERSECTION} if one of the end points of the line segment lies inside of the axis-aligned rectangle; or + * {@link #TWO_INTERSECTION} if the line segment intersects two edges of the axis-aligned rectangle or lies on one edge of the rectangle + */ + public static int intersectLineSegmentAar(float p0X, float p0Y, float p1X, float p1Y, + float minX, float minY, float maxX, float maxY, Vector2f result) { + float dirX = p1X - p0X, dirY = p1Y - p0Y; + float invDirX = 1.0f / dirX, invDirY = 1.0f / dirY; + float tNear, tFar, tymin, tymax; + if (invDirX >= 0.0f) { + tNear = (minX - p0X) * invDirX; + tFar = (maxX - p0X) * invDirX; + } else { + tNear = (maxX - p0X) * invDirX; + tFar = (minX - p0X) * invDirX; + } + if (invDirY >= 0.0f) { + tymin = (minY - p0Y) * invDirY; + tymax = (maxY - p0Y) * invDirY; + } else { + tymin = (maxY - p0Y) * invDirY; + tymax = (minY - p0Y) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return OUTSIDE; + tNear = tymin > tNear || Float.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Float.isNaN(tFar) ? tymax : tFar; + int type = OUTSIDE; + if (tNear <= tFar && tNear <= 1.0f && tFar >= 0.0f) { + if (tNear >= 0.0f && tFar > 1.0f) { + tFar = tNear; + type = ONE_INTERSECTION; + } else if (tNear < 0.0f && tFar <= 1.0f) { + tNear = tFar; + type = ONE_INTERSECTION; + } else if (tNear < 0.0f && tFar > 1.0f) { + type = INSIDE; + } else { + type = TWO_INTERSECTION; + } + result.x = tNear; + result.y = tFar; + } + return type; + } + + /** + * Determine whether the undirected line segment with the end points p0 and p1 + * intersects the axis-aligned rectangle given as its minimum corner min and maximum corner max, + * and store the values of the parameter t in the ray equation p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * into result. + *

+ * This method also detects an intersection of a line segment whose either end point lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * #see {@link #intersectLineSegmentAar(float, float, float, float, float, float, float, float, Vector2f)} + * + * @param p0 + * the line segment's first end point + * @param p1 + * the line segment's second end point + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @param result + * a vector which will hold the values of the parameter t in the ray equation + * p(t) = p0 + t * (p1 - p0) of the near and far point of intersection + * @return {@link #INSIDE} if the line segment lies completely inside of the axis-aligned rectangle; or + * {@link #OUTSIDE} if the line segment lies completely outside of the axis-aligned rectangle; or + * {@link #ONE_INTERSECTION} if one of the end points of the line segment lies inside of the axis-aligned rectangle; or + * {@link #TWO_INTERSECTION} if the line segment intersects two edges of the axis-aligned rectangle + */ + public static int intersectLineSegmentAar(Vector2fc p0, Vector2fc p1, Vector2fc min, Vector2fc max, Vector2f result) { + return intersectLineSegmentAar(p0.x(), p0.y(), p1.x(), p1.y(), min.x(), min.y(), max.x(), max.y(), result); + } + + /** + * Test whether the given ray with the origin (originX, originY) and direction (dirX, dirY) + * intersects the given axis-aligned rectangle given as its minimum corner (minX, minY) and maximum corner (maxX, maxY). + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #testRayAar(Vector2fc, Vector2fc, Vector2fc, Vector2fc) + * + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @return true if the given ray intersects the axis-aligned rectangle; false otherwise + */ + public static boolean testRayAar(float originX, float originY, float dirX, float dirY, float minX, float minY, float maxX, float maxY) { + float invDirX = 1.0f / dirX, invDirY = 1.0f / dirY; + float tNear, tFar, tymin, tymax; + if (invDirX >= 0.0f) { + tNear = (minX - originX) * invDirX; + tFar = (maxX - originX) * invDirX; + } else { + tNear = (maxX - originX) * invDirX; + tFar = (minX - originX) * invDirX; + } + if (invDirY >= 0.0f) { + tymin = (minY - originY) * invDirY; + tymax = (maxY - originY) * invDirY; + } else { + tymin = (maxY - originY) * invDirY; + tymax = (minY - originY) * invDirY; + } + if (tNear > tymax || tymin > tFar) + return false; + tNear = tymin > tNear || Float.isNaN(tNear) ? tymin : tNear; + tFar = tymax < tFar || Float.isNaN(tFar) ? tymax : tFar; + return tNear < tFar && tFar >= 0.0f; + } + + /** + * Test whether the ray with the given origin and direction dir + * intersects the given axis-aligned rectangle specified as its minimum corner min and maximum corner max. + *

+ * This method returns true for a ray whose origin lies inside the axis-aligned rectangle. + *

+ * Reference: An Efficient and Robust Ray–Box Intersection + * + * @see #testRayAar(float, float, float, float, float, float, float, float) + * + * @param origin + * the ray's origin + * @param dir + * the ray's direction + * @param min + * the minimum corner of the axis-aligned rectangle + * @param max + * the maximum corner of the axis-aligned rectangle + * @return true if the given ray intersects the axis-aligned rectangle; false otherwise + */ + public static boolean testRayAar(Vector2fc origin, Vector2fc dir, Vector2fc min, Vector2fc max) { + return testRayAar(origin.x(), origin.y(), dir.x(), dir.y(), min.x(), min.y(), max.x(), max.y()); + } + + /** + * Test whether the given point (pX, pY) lies inside the triangle with the vertices (v0X, v0Y), (v1X, v1Y), (v2X, v2Y). + * + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @return true iff the point lies inside the triangle; false otherwise + */ + public static boolean testPointTriangle(float pX, float pY, float v0X, float v0Y, float v1X, float v1Y, float v2X, float v2Y) { + boolean b1 = (pX - v1X) * (v0Y - v1Y) - (v0X - v1X) * (pY - v1Y) < 0.0f; + boolean b2 = (pX - v2X) * (v1Y - v2Y) - (v1X - v2X) * (pY - v2Y) < 0.0f; + if (b1 != b2) + return false; + boolean b3 = (pX - v0X) * (v2Y - v0Y) - (v2X - v0X) * (pY - v0Y) < 0.0f; + return b2 == b3; + } + + /** + * Test whether the given point lies inside the triangle with the vertices v0, v1, v2. + * + * @param v0 + * the first vertex of the triangle + * @param v1 + * the second vertex of the triangle + * @param v2 + * the third vertex of the triangle + * @param point + * the point + * @return true iff the point lies inside the triangle; false otherwise + */ + public static boolean testPointTriangle(Vector2fc point, Vector2fc v0, Vector2fc v1, Vector2fc v2) { + return testPointTriangle(point.x(), point.y(), v0.x(), v0.y(), v1.x(), v1.y(), v2.x(), v2.y()); + } + + /** + * Test whether the given point (pX, pY) lies inside the axis-aligned rectangle with the minimum corner (minX, minY) + * and maximum corner (maxX, maxY). + * + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param minX + * the x coordinate of the minimum corner of the axis-aligned rectangle + * @param minY + * the y coordinate of the minimum corner of the axis-aligned rectangle + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned rectangle + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned rectangle + * @return true iff the point lies inside the axis-aligned rectangle; false otherwise + */ + public static boolean testPointAar(float pX, float pY, float minX, float minY, float maxX, float maxY) { + return pX >= minX && pY >= minY && pX <= maxX && pY <= maxY; + } + + /** + * Test whether the point (pX, pY) lies inside the circle with center (centerX, centerY) and square radius radiusSquared. + * + * @param pX + * the x coordinate of the point + * @param pY + * the y coordinate of the point + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the square radius of the circle + * @return true iff the point lies inside the circle; false otherwise + */ + public static boolean testPointCircle(float pX, float pY, float centerX, float centerY, float radiusSquared) { + float dx = pX - centerX; + float dy = pY - centerY; + float dx2 = dx * dx; + float dy2 = dy * dy; + return dx2 + dy2 <= radiusSquared; + } + + /** + * Test whether the circle with center (centerX, centerY) and square radius radiusSquared intersects the triangle with counter-clockwise vertices + * (v0X, v0Y), (v1X, v1Y), (v2X, v2Y). + *

+ * The vertices of the triangle must be specified in counter-clockwise order. + *

+ * Reference: http://www.phatcode.net/ + * + * @param centerX + * the x coordinate of the circle's center + * @param centerY + * the y coordinate of the circle's center + * @param radiusSquared + * the square radius of the circle + * @param v0X + * the x coordinate of the first vertex of the triangle + * @param v0Y + * the y coordinate of the first vertex of the triangle + * @param v1X + * the x coordinate of the second vertex of the triangle + * @param v1Y + * the y coordinate of the second vertex of the triangle + * @param v2X + * the x coordinate of the third vertex of the triangle + * @param v2Y + * the y coordinate of the third vertex of the triangle + * @return true iff the circle intersects the triangle; false otherwise + */ + public static boolean testCircleTriangle(float centerX, float centerY, float radiusSquared, float v0X, float v0Y, float v1X, float v1Y, float v2X, float v2Y) { + float c1x = centerX - v0X, c1y = centerY - v0Y; + float c1sqr = c1x * c1x + c1y * c1y - radiusSquared; + if (c1sqr <= 0.0f) + return true; + float c2x = centerX - v1X, c2y = centerY - v1Y; + float c2sqr = c2x * c2x + c2y * c2y - radiusSquared; + if (c2sqr <= 0.0f) + return true; + float c3x = centerX - v2X, c3y = centerY - v2Y; + float c3sqr = c3x * c3x + c3y * c3y - radiusSquared; + if (c3sqr <= 0.0f) + return true; + float e1x = v1X - v0X, e1y = v1Y - v0Y; + float e2x = v2X - v1X, e2y = v2Y - v1Y; + float e3x = v0X - v2X, e3y = v0Y - v2Y; + if (e1x * c1y - e1y * c1x >= 0.0f && e2x * c2y - e2y * c2x >= 0.0f && e3x * c3y - e3y * c3x >= 0.0f) + return true; + float k = c1x * e1x + c1y * e1y; + if (k >= 0.0f) { + float len = e1x * e1x + e1y * e1y; + if (k <= len) { + if (c1sqr * len <= k * k) + return true; + } + } + k = c2x * e2x + c2y * e2y; + if (k > 0.0f) { + float len = e2x * e2x + e2y * e2y; + if (k <= len) { + if (c2sqr * len <= k * k) + return true; + } + } + k = c3x * e3x + c3y * e3y; + if (k >= 0.0f) { + float len = e3x * e3x + e3y * e3y; + if (k < len) { + if (c3sqr * len <= k * k) + return true; + } + } + return false; + } + + /** + * Test whether the circle with given center and square radius radiusSquared intersects the triangle with counter-clockwise vertices + * v0, v1, v2. + *

+ * The vertices of the triangle must be specified in counter-clockwise order. + *

+ * Reference: http://www.phatcode.net/ + * + * @param center + * the circle's center + * @param radiusSquared + * the square radius of the circle + * @param v0 + * the first vertex of the triangle + * @param v1 + * the second vertex of the triangle + * @param v2 + * the third vertex of the triangle + * @return true iff the circle intersects the triangle; false otherwise + */ + public static boolean testCircleTriangle(Vector2fc center, float radiusSquared, Vector2fc v0, Vector2fc v1, Vector2fc v2) { + return testCircleTriangle(center.x(), center.y(), radiusSquared, v0.x(), v0.y(), v1.x(), v1.y(), v2.x(), v2.y()); + } + + /** + * Determine whether the polygon specified by the given sequence of (x, y) coordinate pairs intersects with the ray + * with given origin (originX, originY, originZ) and direction (dirX, dirY, dirZ), and store the point of intersection + * into the given vector p. + *

+ * If the polygon intersects the ray, this method returns the index of the polygon edge intersecting the ray, that is, the index of the + * first vertex of the directed line segment. The second vertex is always that index + 1, modulus the number of polygon vertices. + * + * @param verticesXY + * the sequence of (x, y) coordinate pairs of all vertices of the polygon + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param p + * will hold the point of intersection + * @return the index of the first vertex of the polygon edge that intersects the ray; or -1 if the ray does not intersect the polygon + */ + public static int intersectPolygonRay(float[] verticesXY, float originX, float originY, float dirX, float dirY, Vector2f p) { + float nearestT = Float.POSITIVE_INFINITY; + int count = verticesXY.length >> 1; + int edgeIndex = -1; + float aX = verticesXY[(count-1)<<1], aY = verticesXY[((count-1)<<1) + 1]; + for (int i = 0; i < count; i++) { + float bX = verticesXY[i << 1], bY = verticesXY[(i << 1) + 1]; + float doaX = originX - aX, doaY = originY - aY; + float dbaX = bX - aX, dbaY = bY - aY; + float invDbaDir = 1.0f / (dbaY * dirX - dbaX * dirY); + float t = (dbaX * doaY - dbaY * doaX) * invDbaDir; + if (t >= 0.0f && t < nearestT) { + float t2 = (doaY * dirX - doaX * dirY) * invDbaDir; + if (t2 >= 0.0f && t2 <= 1.0f) { + edgeIndex = (i - 1 + count) % count; + nearestT = t; + p.x = originX + t * dirX; + p.y = originY + t * dirY; + } + } + aX = bX; + aY = bY; + } + return edgeIndex; + } + + /** + * Determine whether the polygon specified by the given sequence of vertices intersects with the ray + * with given origin (originX, originY, originZ) and direction (dirX, dirY, dirZ), and store the point of intersection + * into the given vector p. + *

+ * If the polygon intersects the ray, this method returns the index of the polygon edge intersecting the ray, that is, the index of the + * first vertex of the directed line segment. The second vertex is always that index + 1, modulus the number of polygon vertices. + * + * @param vertices + * the sequence of (x, y) coordinate pairs of all vertices of the polygon + * @param originX + * the x coordinate of the ray's origin + * @param originY + * the y coordinate of the ray's origin + * @param dirX + * the x coordinate of the ray's direction + * @param dirY + * the y coordinate of the ray's direction + * @param p + * will hold the point of intersection + * @return the index of the first vertex of the polygon edge that intersects the ray; or -1 if the ray does not intersect the polygon + */ + public static int intersectPolygonRay(Vector2fc[] vertices, float originX, float originY, float dirX, float dirY, Vector2f p) { + float nearestT = Float.POSITIVE_INFINITY; + int count = vertices.length; + int edgeIndex = -1; + float aX = vertices[count-1].x(), aY = vertices[count-1].y(); + for (int i = 0; i < count; i++) { + Vector2fc b = vertices[i]; + float bX = b.x(), bY = b.y(); + float doaX = originX - aX, doaY = originY - aY; + float dbaX = bX - aX, dbaY = bY - aY; + float invDbaDir = 1.0f / (dbaY * dirX - dbaX * dirY); + float t = (dbaX * doaY - dbaY * doaX) * invDbaDir; + if (t >= 0.0f && t < nearestT) { + float t2 = (doaY * dirX - doaX * dirY) * invDbaDir; + if (t2 >= 0.0f && t2 <= 1.0f) { + edgeIndex = (i - 1 + count) % count; + nearestT = t; + p.x = originX + t * dirX; + p.y = originY + t * dirY; + } + } + aX = bX; + aY = bY; + } + return edgeIndex; + } + + /** + * Determine whether the two lines, specified via two points lying on each line, intersect each other, and store the point of intersection + * into the given vector p. + * + * @param ps1x + * the x coordinate of the first point on the first line + * @param ps1y + * the y coordinate of the first point on the first line + * @param pe1x + * the x coordinate of the second point on the first line + * @param pe1y + * the y coordinate of the second point on the first line + * @param ps2x + * the x coordinate of the first point on the second line + * @param ps2y + * the y coordinate of the first point on the second line + * @param pe2x + * the x coordinate of the second point on the second line + * @param pe2y + * the y coordinate of the second point on the second line + * @param p + * will hold the point of intersection + * @return true iff the two lines intersect; false otherwise + */ + public static boolean intersectLineLine(float ps1x, float ps1y, float pe1x, float pe1y, float ps2x, float ps2y, float pe2x, float pe2y, Vector2f p) { + float d1x = ps1x - pe1x; + float d1y = pe1y - ps1y; + float d1ps1 = d1y * ps1x + d1x * ps1y; + float d2x = ps2x - pe2x; + float d2y = pe2y - ps2y; + float d2ps2 = d2y * ps2x + d2x * ps2y; + float det = d1y * d2x - d2y * d1x; + if (det == 0.0f) + return false; + p.x = (d2x * d1ps1 - d1x * d2ps2) / det; + p.y = (d1y * d2ps2 - d2y * d1ps1) / det; + return true; + } + + private static boolean separatingAxis(Vector2f[] v1s, Vector2f[] v2s, float aX, float aY) { + float minA = Float.POSITIVE_INFINITY, maxA = Float.NEGATIVE_INFINITY; + float minB = Float.POSITIVE_INFINITY, maxB = Float.NEGATIVE_INFINITY; + int maxLen = Math.max(v1s.length, v2s.length); + /* Project both polygons on axis */ + for (int k = 0; k < maxLen; k++) { + if (k < v1s.length) { + Vector2f v1 = v1s[k]; + float d = v1.x * aX + v1.y * aY; + if (d < minA) minA = d; + if (d > maxA) maxA = d; + } + if (k < v2s.length) { + Vector2f v2 = v2s[k]; + float d = v2.x * aX + v2.y * aY; + if (d < minB) minB = d; + if (d > maxB) maxB = d; + } + /* Early-out if overlap found */ + if (minA <= maxB && minB <= maxA) { + return false; + } + } + return true; + } + + /** + * Test if the two convex polygons, given via their vertices, intersect. + * + * @param v1s + * the vertices of the first convex polygon + * @param v2s + * the vertices of the second convex polygon + * @return true if the convex polygons intersect; false otherwise + */ + public static boolean testPolygonPolygon(Vector2f[] v1s, Vector2f[] v2s) { + /* Try to find a separating axis using the first polygon's edges */ + for (int i = 0, j = v1s.length - 1; i < v1s.length; j = i, i++) { + Vector2f s = v1s[i], t = v1s[j]; + if (separatingAxis(v1s, v2s, s.y - t.y, t.x - s.x)) + return false; + } + /* Try to find a separating axis using the second polygon's edges */ + for (int i = 0, j = v2s.length - 1; i < v2s.length; j = i, i++) { + Vector2f s = v2s[i], t = v2s[j]; + if (separatingAxis(v1s, v2s, s.y - t.y, t.x - s.x)) + return false; + } + return true; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Math.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Math.java new file mode 100644 index 000000000..147dd0af5 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Math.java @@ -0,0 +1,566 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Contains fast approximations of some {@link java.lang.Math} operations. + *

+ * By default, {@link java.lang.Math} methods will be used by all other JOML classes. In order to use the approximations in this class, start the JVM with the parameter -Djoml.fastmath. + *

+ * There are two algorithms for approximating sin/cos: + *

    + *
  1. arithmetic polynomial approximation contributed by roquendm + *
  2. theagentd's linear interpolation variant of Riven's algorithm from + * http://www.java-gaming.org/ + *
+ * By default, the first algorithm is being used. In order to use the second one, start the JVM with -Djoml.sinLookup. The lookup table bit length of the second algorithm can also be adjusted + * for improved accuracy via -Djoml.sinLookup.bits=<n>, where <n> is the number of bits of the lookup table. + * + * @author Kai Burjack + */ +public class Math { + + /* + * The following implementation of an approximation of sine and cosine was + * thankfully donated by Riven from http://java-gaming.org/. + * + * The code for linear interpolation was gratefully donated by theagentd + * from the same site. + */ + public static final double PI = java.lang.Math.PI; + static final double PI2 = PI * 2.0; + static final float PI_f = (float) java.lang.Math.PI; + static final float PI2_f = PI_f * 2.0f; + static final double PIHalf = PI * 0.5; + static final float PIHalf_f = (float) (PI * 0.5); + static final double PI_4 = PI * 0.25; + static final double PI_INV = 1.0 / PI; + private static final int lookupBits = Options.SIN_LOOKUP_BITS; + private static final int lookupTableSize = 1 << lookupBits; + private static final int lookupTableSizeMinus1 = lookupTableSize - 1; + private static final int lookupTableSizeWithMargin = lookupTableSize + 1; + private static final float pi2OverLookupSize = PI2_f / lookupTableSize; + private static final float lookupSizeOverPi2 = lookupTableSize / PI2_f; + private static final float sinTable[]; + static { + if (Options.FASTMATH && Options.SIN_LOOKUP) { + sinTable = new float[lookupTableSizeWithMargin]; + for (int i = 0; i < lookupTableSizeWithMargin; i++) { + double d = i * pi2OverLookupSize; + sinTable[i] = (float) java.lang.Math.sin(d); + } + } else { + sinTable = null; + } + } + + private static final double c1 = Double.longBitsToDouble(-4628199217061079772L); + private static final double c2 = Double.longBitsToDouble(4575957461383582011L); + private static final double c3 = Double.longBitsToDouble(-4671919876300759001L); + private static final double c4 = Double.longBitsToDouble(4523617214285661942L); + private static final double c5 = Double.longBitsToDouble(-4730215272828025532L); + private static final double c6 = Double.longBitsToDouble(4460272573143870633L); + private static final double c7 = Double.longBitsToDouble(-4797767418267846529L); + + /** + * @author theagentd + */ + static double sin_theagentd_arith(double x){ + double xi = floor((x + PI_4) * PI_INV); + double x_ = x - xi * PI; + double sign = ((int)xi & 1) * -2 + 1; + double x2 = x_ * x_; + double sin = x_; + double tx = x_ * x2; + sin += tx * c1; tx *= x2; + sin += tx * c2; tx *= x2; + sin += tx * c3; tx *= x2; + sin += tx * c4; tx *= x2; + sin += tx * c5; tx *= x2; + sin += tx * c6; tx *= x2; + sin += tx * c7; + return sign * sin; + } + + /** + * Reference: http://www.java-gaming.org/ + */ + static double sin_roquen_arith(double x) { + double xi = Math.floor((x + PI_4) * PI_INV); + double x_ = x - xi * PI; + double sign = ((int)xi & 1) * -2 + 1; + double x2 = x_ * x_; + + // code from sin_theagentd_arith: + // double sin = x_; + // double tx = x_ * x2; + // sin += tx * c1; tx *= x2; + // sin += tx * c2; tx *= x2; + // sin += tx * c3; tx *= x2; + // sin += tx * c4; tx *= x2; + // sin += tx * c5; tx *= x2; + // sin += tx * c6; tx *= x2; + // sin += tx * c7; + // return sign * sin; + + double sin; + x_ = sign*x_; + sin = c7; + sin = sin*x2 + c6; + sin = sin*x2 + c5; + sin = sin*x2 + c4; + sin = sin*x2 + c3; + sin = sin*x2 + c2; + sin = sin*x2 + c1; + return x_ + x_*x2*sin; + } + + private static final double s5 = Double.longBitsToDouble(4523227044276562163L); + private static final double s4 = Double.longBitsToDouble(-4671934770969572232L); + private static final double s3 = Double.longBitsToDouble(4575957211482072852L); + private static final double s2 = Double.longBitsToDouble(-4628199223918090387L); + private static final double s1 = Double.longBitsToDouble(4607182418589157889L); + + /** + * Reference: http://www.java-gaming.org/ + */ + static double sin_roquen_9(double v) { + double i = java.lang.Math.rint(v*PI_INV); + double x = v - i * Math.PI; + double qs = 1-2*((int)i & 1); + double x2 = x*x; + double r; + x = qs*x; + r = s5; + r = r*x2 + s4; + r = r*x2 + s3; + r = r*x2 + s2; + r = r*x2 + s1; + return x*r; + } + + private static final double k1 = Double.longBitsToDouble(-4628199217061079959L); + private static final double k2 = Double.longBitsToDouble(4575957461383549981L); + private static final double k3 = Double.longBitsToDouble(-4671919876307284301L); + private static final double k4 = Double.longBitsToDouble(4523617213632129738L); + private static final double k5 = Double.longBitsToDouble(-4730215344060517252L); + private static final double k6 = Double.longBitsToDouble(4460268259291226124L); + private static final double k7 = Double.longBitsToDouble(-4798040743777455072L); + + /** + * Reference: http://www.java-gaming.org/ + */ + static double sin_roquen_newk(double v) { + double i = java.lang.Math.rint(v*PI_INV); + double x = v - i * Math.PI; + double qs = 1-2*((int)i & 1); + double x2 = x*x; + double r; + x = qs*x; + r = k7; + r = r*x2 + k6; + r = r*x2 + k5; + r = r*x2 + k4; + r = r*x2 + k3; + r = r*x2 + k2; + r = r*x2 + k1; + return x + x*x2*r; + } + + /** + * Reference: http://www.java-gaming.org/ + */ + static float sin_theagentd_lookup(float rad) { + float index = rad * lookupSizeOverPi2; + int ii = (int)java.lang.Math.floor(index); + float alpha = index - ii; + int i = ii & lookupTableSizeMinus1; + float sin1 = sinTable[i]; + float sin2 = sinTable[i + 1]; + return sin1 + (sin2 - sin1) * alpha; + } + + public static float sin(float rad) { + return (float) java.lang.Math.sin(rad); + } + public static double sin(double rad) { + if (Options.FASTMATH) { + if (Options.SIN_LOOKUP) + return sin_theagentd_lookup((float) rad); + return sin_roquen_newk(rad); + } + return java.lang.Math.sin(rad); + } + + public static float cos(float rad) { + if (Options.FASTMATH) + return sin(rad + PIHalf_f); + return (float) java.lang.Math.cos(rad); + } + public static double cos(double rad) { + if (Options.FASTMATH) + return sin(rad + PIHalf); + return java.lang.Math.cos(rad); + } + + public static float cosFromSin(float sin, float angle) { + if (Options.FASTMATH) + return sin(angle + PIHalf_f); + return cosFromSinInternal(sin, angle); + } + private static float cosFromSinInternal(float sin, float angle) { + // sin(x)^2 + cos(x)^2 = 1 + float cos = sqrt(1.0f - sin * sin); + float a = angle + PIHalf_f; + float b = a - (int)(a / PI2_f) * PI2_f; + if (b < 0.0) + b = PI2_f + b; + if (b >= PI_f) + return -cos; + return cos; + } + public static double cosFromSin(double sin, double angle) { + if (Options.FASTMATH) + return sin(angle + PIHalf); + // sin(x)^2 + cos(x)^2 = 1 + double cos = sqrt(1.0 - sin * sin); + double a = angle + PIHalf; + double b = a - (int)(a / PI2) * PI2; + if (b < 0.0) + b = PI2 + b; + if (b >= PI) + return -cos; + return cos; + } + + /* Other math functions not yet approximated */ + + public static float sqrt(float r) { + return (float) java.lang.Math.sqrt(r); + } + public static double sqrt(double r) { + return java.lang.Math.sqrt(r); + } + + public static float invsqrt(float r) { + return 1.0f / (float) java.lang.Math.sqrt(r); + } + public static double invsqrt(double r) { + return 1.0 / java.lang.Math.sqrt(r); + } + + public static float tan(float r) { + return (float) java.lang.Math.tan(r); + } + public static double tan(double r) { + return java.lang.Math.tan(r); + } + + public static float acos(float r) { + return (float) java.lang.Math.acos(r); + } + public static double acos(double r) { + return java.lang.Math.acos(r); + } + + public static float safeAcos(float v) { + if (v < -1.0f) + return Math.PI_f; + else if (v > +1.0f) + return 0.0f; + else + return acos(v); + } + public static double safeAcos(double v) { + if (v < -1.0) + return Math.PI; + else if (v > +1.0) + return 0.0; + else + return acos(v); + } + + /** + * https://math.stackexchange.com/questions/1098487/atan2-faster-approximation/1105038#answer-1105038 + */ + private static double fastAtan2(double y, double x) { + double ax = x >= 0.0 ? x : -x, ay = y >= 0.0 ? y : -y; + double a = min(ax, ay) / max(ax, ay); + double s = a * a; + double r = ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a; + if (ay > ax) + r = 1.57079637 - r; + if (x < 0.0) + r = 3.14159274 - r; + return y >= 0 ? r : -r; + } + + public static float atan2(float y, float x) { + return (float) java.lang.Math.atan2(y, x); + } + public static double atan2(double y, double x) { + if (Options.FASTMATH) + return fastAtan2(y, x); + return java.lang.Math.atan2(y, x); + } + + public static float asin(float r) { + return (float) java.lang.Math.asin(r); + } + public static double asin(double r) { + return java.lang.Math.asin(r); + } + public static float safeAsin(float r) { + return r <= -1.0f ? -PIHalf_f : r >= 1.0f ? PIHalf_f : asin(r); + } + public static double safeAsin(double r) { + return r <= -1.0 ? -PIHalf : r >= 1.0 ? PIHalf : asin(r); + } + + public static float abs(float r) { + return java.lang.Math.abs(r); + } + public static double abs(double r) { + return java.lang.Math.abs(r); + } + + static boolean absEqualsOne(float r) { + return (Float.floatToRawIntBits(r) & 0x7FFFFFFF) == 0x3F800000; + } + static boolean absEqualsOne(double r) { + return (Double.doubleToRawLongBits(r) & 0x7FFFFFFFFFFFFFFFL) == 0x3FF0000000000000L; + } + + public static int abs(int r) { + return java.lang.Math.abs(r); + } + + public static int max(int x, int y) { + return java.lang.Math.max(x, y); + } + + public static int min(int x, int y) { + return java.lang.Math.min(x, y); + } + + public static double min(double a, double b) { + return a < b ? a : b; + } + public static float min(float a, float b) { + return a < b ? a : b; + } + + public static float max(float a, float b) { + return a > b ? a : b; + } + public static double max(double a, double b) { + return a > b ? a : b; + } + + public static float clamp(float a, float b, float val){ + return max(a,min(b,val)); + } + public static double clamp(double a, double b, double val) { + return max(a,min(b,val)); + } + public static int clamp(int a, int b, int val) { + return max(a, min(b, val)); + } + + public static float toRadians(float angles) { + return (float) java.lang.Math.toRadians(angles); + } + public static double toRadians(double angles) { + return java.lang.Math.toRadians(angles); + } + + public static double toDegrees(double angles) { + return java.lang.Math.toDegrees(angles); + } + + public static double floor(double v) { + return java.lang.Math.floor(v); + } + + public static float floor(float v) { + return (float) java.lang.Math.floor(v); + } + + public static double ceil(double v) { + return java.lang.Math.ceil(v); + } + + public static float ceil(float v) { + return (float) java.lang.Math.ceil(v); + } + + public static long round(double v) { + return java.lang.Math.round(v); + } + + public static int round(float v) { + return java.lang.Math.round(v); + } + + public static double exp(double a) { + return java.lang.Math.exp(a); + } + + public static boolean isFinite(double d) { + return abs(d) <= Double.MAX_VALUE; + } + + public static boolean isFinite(float f) { + return abs(f) <= Float.MAX_VALUE; + } + + public static float fma(float a, float b, float c) { + if (Runtime.HAS_Math_fma) + return java.lang.Math.fma(a, b, c); + return a * b + c; + } + + public static double fma(double a, double b, double c) { + if (Runtime.HAS_Math_fma) + return java.lang.Math.fma(a, b, c); + return a * b + c; + } + + public static int roundUsing(float v, int mode) { + switch (mode) { + case RoundingMode.TRUNCATE: + return (int) v; + case RoundingMode.CEILING: + return (int) java.lang.Math.ceil(v); + case RoundingMode.FLOOR: + return (int) java.lang.Math.floor(v); + case RoundingMode.HALF_DOWN: + return roundHalfDown(v); + case RoundingMode.HALF_UP: + return roundHalfUp(v); + case RoundingMode.HALF_EVEN: + return roundHalfEven(v); + default: + throw new UnsupportedOperationException(); + } + } + public static int roundUsing(double v, int mode) { + switch (mode) { + case RoundingMode.TRUNCATE: + return (int) v; + case RoundingMode.CEILING: + return (int) java.lang.Math.ceil(v); + case RoundingMode.FLOOR: + return (int) java.lang.Math.floor(v); + case RoundingMode.HALF_DOWN: + return roundHalfDown(v); + case RoundingMode.HALF_UP: + return roundHalfUp(v); + case RoundingMode.HALF_EVEN: + return roundHalfEven(v); + default: + throw new UnsupportedOperationException(); + } + } + + public static float lerp(float a, float b, float t){ + return Math.fma(b - a, t, a); + } + public static double lerp(double a, double b, double t) { + return Math.fma(b - a, t, a); + } + + public static float biLerp(float q00, float q10, float q01, float q11, float tx, float ty) { + float lerpX1 = lerp(q00, q10, tx); + float lerpX2 = lerp(q01, q11, tx); + return lerp(lerpX1, lerpX2, ty); + } + + public static double biLerp(double q00, double q10, double q01, double q11, double tx, double ty) { + double lerpX1 = lerp(q00, q10, tx); + double lerpX2 = lerp(q01, q11, tx); + return lerp(lerpX1, lerpX2, ty); + } + + public static float triLerp(float q000, float q100, float q010, float q110, float q001, float q101, float q011, float q111, float tx, float ty, float tz) { + float x00 = lerp(q000, q100, tx); + float x10 = lerp(q010, q110, tx); + float x01 = lerp(q001, q101, tx); + float x11 = lerp(q011, q111, tx); + float y0 = lerp(x00, x10, ty); + float y1 = lerp(x01, x11, ty); + return lerp(y0, y1, tz); + } + + public static double triLerp(double q000, double q100, double q010, double q110, double q001, double q101, double q011, double q111, double tx, double ty, double tz) { + double x00 = lerp(q000, q100, tx); + double x10 = lerp(q010, q110, tx); + double x01 = lerp(q001, q101, tx); + double x11 = lerp(q011, q111, tx); + double y0 = lerp(x00, x10, ty); + double y1 = lerp(x01, x11, ty); + return lerp(y0, y1, tz); + } + + public static int roundHalfEven(float v) { + return (int) java.lang.Math.rint(v); + } + public static int roundHalfDown(float v) { + return (v > 0) ? (int) java.lang.Math.ceil(v - 0.5d) : (int) java.lang.Math.floor(v + 0.5d); + } + public static int roundHalfUp(float v) { + return (v > 0) ? (int) java.lang.Math.floor(v + 0.5d) : (int) java.lang.Math.ceil(v - 0.5d); + } + + public static int roundHalfEven(double v) { + return (int) java.lang.Math.rint(v); + } + public static int roundHalfDown(double v) { + return (v > 0) ? (int) java.lang.Math.ceil(v - 0.5d) : (int) java.lang.Math.floor(v + 0.5d); + } + public static int roundHalfUp(double v) { + return (v > 0) ? (int) java.lang.Math.floor(v + 0.5d) : (int) java.lang.Math.ceil(v - 0.5d); + } + + public static double random() { + return java.lang.Math.random(); + } + + public static double signum(double v) { + return java.lang.Math.signum(v); + } + public static float signum(float v) { + return java.lang.Math.signum(v); + } + public static int signum(int v) { + int r; + r = Integer.signum(v); + return r; + } + public static int signum(long v) { + int r; + r = Long.signum(v); + return r; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2d.java new file mode 100644 index 000000000..e589a5fea --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2d.java @@ -0,0 +1,1536 @@ +/* + * The MIT License + * + * Copyright (c) 2020-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a 2x2 matrix of doubles, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10
+ * m01 m11
+ * + * @author Joseph Burton + */ +public class Matrix2d implements Externalizable, Cloneable, Matrix2dc { + + private static final long serialVersionUID = 1L; + + public double m00, m01; + public double m10, m11; + + /** + * Create a new {@link Matrix2d} and set it to {@link #identity() identity}. + */ + public Matrix2d() { + m00 = 1.0; + m11 = 1.0; + } + + /** + * Create a new {@link Matrix2d} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix2dc} to copy the values from + */ + public Matrix2d(Matrix2dc mat) { + if (mat instanceof Matrix2d) { + MemUtil.INSTANCE.copy((Matrix2d) mat, this); + } else { + setMatrix2dc(mat); + } + } + + /** + * Create a new {@link Matrix2d} and initialize it with the values from the given matrix. + * + * @param mat + * the matrix to initialize this matrix with + */ + public Matrix2d(Matrix2fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Create a new {@link Matrix2d} and make it a copy of the upper left 2x2 of the given {@link Matrix3dc}. + * + * @param mat + * the {@link Matrix3dc} to copy the values from + */ + public Matrix2d(Matrix3dc mat) { + if (mat instanceof Matrix3d) { + MemUtil.INSTANCE.copy((Matrix3d) mat, this); + } else { + setMatrix3dc(mat); + } + } + + /** + * Create a new {@link Matrix2d} and make it a copy of the upper left 2x2 of the given {@link Matrix3fc}. + * + * @param mat + * the {@link Matrix3fc} to copy the values from + */ + public Matrix2d(Matrix3fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Create a new 2x2 matrix using the supplied double values. The order of the parameter is column-major, + * so the first two parameters specify the two elements of the first column. + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + */ + public Matrix2d(double m00, double m01, + double m10, double m11) { + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + } + + /** + * Create a new {@link Matrix2d} by reading its 4 double components from the given {@link DoubleBuffer} + * at the buffer's current position. + *

+ * That DoubleBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link DoubleBuffer} to read the matrix values from + */ + public Matrix2d(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Matrix2d} and initialize its two columns using the supplied vectors. + * + * @param col0 + * the first column + * @param col1 + * the second column + */ + public Matrix2d(Vector2dc col0, Vector2dc col1) { + m00 = col0.x(); + m01 = col0.y(); + m10 = col1.x(); + m11 = col1.y(); + } + + public double m00() { + return m00; + } + public double m01() { + return m01; + } + public double m10() { + return m10; + } + public double m11() { + return m11; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + public Matrix2d m00(double m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + public Matrix2d m01(double m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + public Matrix2d m10(double m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + public Matrix2d m11(double m11) { + this.m11 = m11; + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + Matrix2d _m00(double m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + Matrix2d _m01(double m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + Matrix2d _m10(double m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + Matrix2d _m11(double m11) { + this.m11 = m11; + return this; + } + + /** + * Set the elements of this matrix to the ones in m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix2d set(Matrix2dc m) { + if (m instanceof Matrix2d) { + MemUtil.INSTANCE.copy((Matrix2d) m, this); + } else { + setMatrix2dc(m); + } + return this; + } + private void setMatrix2dc(Matrix2dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Set the elements of this matrix to the ones in m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix2d set(Matrix2fc m) { + m00 = m.m00(); + m01 = m.m01(); + m10 = m.m10(); + m11 = m.m11(); + return this; + } + + /** + * Set the elements of this matrix to the left 2x2 submatrix of m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix2d set(Matrix3x2dc m) { + if (m instanceof Matrix3x2d) { + MemUtil.INSTANCE.copy((Matrix3x2d) m, this); + } else { + setMatrix3x2dc(m); + } + return this; + } + private void setMatrix3x2dc(Matrix3x2dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Set the elements of this matrix to the left 2x2 submatrix of m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix2d set(Matrix3x2fc m) { + m00 = m.m00(); + m01 = m.m01(); + m10 = m.m10(); + m11 = m.m11(); + return this; + } + + /** + * Set the elements of this matrix to the upper left 2x2 of the given {@link Matrix3dc}. + * + * @param m + * the {@link Matrix3dc} to copy the values from + * @return this + */ + public Matrix2d set(Matrix3dc m) { + if (m instanceof Matrix3d) { + MemUtil.INSTANCE.copy((Matrix3d) m, this); + } else { + setMatrix3dc(m); + } + return this; + } + private void setMatrix3dc(Matrix3dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Set the elements of this matrix to the upper left 2x2 of the given {@link Matrix3dc}. + * + * @param m + * the {@link Matrix3fc} to copy the values from + * @return this + */ + public Matrix2d set(Matrix3fc m) { + m00 = m.m00(); + m01 = m.m01(); + m10 = m.m10(); + m11 = m.m11(); + return this; + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix2d mul(Matrix2dc right) { + return mul(right, this); + } + + public Matrix2d mul(Matrix2dc right, Matrix2d dest) { + double nm00 = m00 * right.m00() + m10 * right.m01(); + double nm01 = m01 * right.m00() + m11 * right.m01(); + double nm10 = m00 * right.m10() + m10 * right.m11(); + double nm11 = m01 * right.m10() + m11 * right.m11(); + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix2d mul(Matrix2fc right) { + return mul(right, this); + } + + public Matrix2d mul(Matrix2fc right, Matrix2d dest) { + double nm00 = m00 * right.m00() + m10 * right.m01(); + double nm01 = m01 * right.m00() + m11 * right.m01(); + double nm10 = m00 * right.m10() + m10 * right.m11(); + double nm11 = m01 * right.m10() + m11 * right.m11(); + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in this. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @return this + */ + public Matrix2d mulLocal(Matrix2dc left) { + return mulLocal(left, this); + } + + public Matrix2d mulLocal(Matrix2dc left, Matrix2d dest) { + double nm00 = left.m00() * m00 + left.m10() * m01; + double nm01 = left.m01() * m00 + left.m11() * m01; + double nm10 = left.m00() * m10 + left.m10() * m11; + double nm11 = left.m01() * m10 + left.m11() * m11; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Set the values within this matrix to the supplied double values. The result looks like this: + *

+ * m00, m10
+ * m01, m11
+ * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @return this + */ + public Matrix2d set(double m00, double m01, + double m10, double m11) { + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + return this; + } + + /** + * Set the values in this matrix based on the supplied double array. The result looks like this: + *

+ * 0, 2
+ * 1, 3
+ * + * This method only uses the first 4 values, all others are ignored. + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix2d set(double m[]) { + MemUtil.INSTANCE.copy(m, 0, this); + return this; + } + + /** + * Set the two columns of this matrix to the supplied vectors, respectively. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @return this + */ + public Matrix2d set(Vector2dc col0, Vector2dc col1) { + m00 = col0.x(); + m01 = col0.y(); + m10 = col1.x(); + m11 = col1.y(); + return this; + } + + public double determinant() { + return m00 * m11 - m10 * m01; + } + + /** + * Invert this matrix. + * + * @return this + */ + public Matrix2d invert() { + return invert(this); + } + + public Matrix2d invert(Matrix2d dest) { + double s = 1.0 / determinant(); + double nm00 = m11 * s; + double nm01 = -m01 * s; + double nm10 = -m10 * s; + double nm11 = m00 * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Transpose this matrix. + * + * @return this + */ + public Matrix2d transpose() { + return transpose(this); + } + + public Matrix2d transpose(Matrix2d dest) { + dest.set(m00, m10, + m01, m11); + return dest; + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix2dc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix2dc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix2d get(Matrix2d dest) { + return dest.set(this); + } + + public Matrix3x2d get(Matrix3x2d dest) { + return dest.set(this); + } + + public Matrix3d get(Matrix3d dest) { + return dest.set(this); + } + + public double getRotation() { + return (double) Math.atan2(m01, m11); + } + + + public DoubleBuffer get(DoubleBuffer buffer) { + return get(buffer.position(), buffer); + } + + public DoubleBuffer get(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public DoubleBuffer getTransposed(DoubleBuffer buffer) { + return get(buffer.position(), buffer); + } + + public DoubleBuffer getTransposed(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public ByteBuffer getTransposed(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + public ByteBuffer getTransposed(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public Matrix2dc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + public double[] get(double[] arr, int offset) { + MemUtil.INSTANCE.copy(this, arr, offset); + return arr; + } + + public double[] get(double[] arr) { + return get(arr, 0); + } + + /** + * Set the values of this matrix by reading 4 double values from the given {@link DoubleBuffer} in column-major order, + * starting at its current position. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix2d set(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 4 double values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix2d set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 4 double values from the given {@link DoubleBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix2d set(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 4 double values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix2d set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + /** + * Set the values of this matrix by reading 4 double values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix2d setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set all values within this matrix to zero. + * + * @return this + */ + public Matrix2d zero() { + MemUtil.INSTANCE.zero(this); + return this; + } + + /** + * Set this matrix to the identity. + * + * @return this + */ + public Matrix2d identity() { + m00 = 1.0; + m01 = 0.0; + m10 = 0.0; + m11 = 1.0; + return this; + } + + public Matrix2d scale(Vector2dc xy, Matrix2d dest) { + return scale(xy.x(), xy.y(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy.x and + * xy.y factors, respectively. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @return this + */ + public Matrix2d scale(Vector2dc xy) { + return scale(xy.x(), xy.y(), this); + } + + public Matrix2d scale(double x, double y, Matrix2d dest) { + // scale matrix elements: + // m00 = x, m11 = y + // all others = 0 + dest.m00 = m00 * x; + dest.m01 = m01 * x; + dest.m10 = m10 * y; + dest.m11 = m11 * y; + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x and + * y factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix2d scale(double x, double y) { + return scale(x, y, this); + } + + public Matrix2d scale(double xy, Matrix2d dest) { + return scale(xy, xy, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xy factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(double, double) + * + * @param xy + * the factor for all components + * @return this + */ + public Matrix2d scale(double xy) { + return scale(xy, xy); + } + + public Matrix2d scaleLocal(double x, double y, Matrix2d dest) { + dest.m00 = x * m00; + dest.m01 = y * m01; + dest.m10 = x * m10; + dest.m11 = y * m11; + return dest; + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x and + * y factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix2d scaleLocal(double x, double y) { + return scaleLocal(x, y, this); + } + + /** + * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(double) scale()} instead. + * + * @see #scale(double) + * + * @param factor + * the scale factor in x and y + * @return this + */ + public Matrix2d scaling(double factor) { + MemUtil.INSTANCE.zero(this); + m00 = factor; + m11 = factor; + return this; + } + + /** + * Set this matrix to be a simple scale matrix. + * + * @param x + * the scale in x + * @param y + * the scale in y + * @return this + */ + public Matrix2d scaling(double x, double y) { + MemUtil.INSTANCE.zero(this); + m00 = x; + m11 = y; + return this; + } + + /** + * Set this matrix to be a simple scale matrix which scales the base axes by xy.x and xy.y respectively. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix use {@link #scale(Vector2dc) scale()} instead. + * + * @see #scale(Vector2dc) + * + * @param xy + * the scale in x and y respectively + * @return this + */ + public Matrix2d scaling(Vector2dc xy) { + return scaling(xy.x(), xy.y()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about the origin. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to post-multiply a rotation transformation directly to a + * matrix, use {@link #rotate(double) rotate()} instead. + * + * @see #rotate(double) + * + * @param angle + * the angle in radians + * @return this + */ + public Matrix2d rotation(double angle) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + m00 = cos; + m01 = sin; + m10 = -sin; + m11 = cos; + return this; + } + + public Vector2d transform(Vector2d v) { + return v.mul(this); + } + + public Vector2d transform(Vector2dc v, Vector2d dest) { + v.mul(this, dest); + return dest; + } + + public Vector2d transform(double x, double y, Vector2d dest) { + dest.set(m00 * x + m10 * y, + m01 * x + m11 * y); + return dest; + } + + public Vector2d transformTranspose(Vector2d v) { + return v.mulTranspose(this); + } + + public Vector2d transformTranspose(Vector2dc v, Vector2d dest) { + v.mulTranspose(this, dest); + return dest; + } + + public Vector2d transformTranspose(double x, double y, Vector2d dest) { + dest.set(m00 * x + m01 * y, + m10 * x + m11 * y); + return dest; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(m00); + out.writeDouble(m01); + out.writeDouble(m10); + out.writeDouble(m11); + } + + public void readExternal(ObjectInput in) throws IOException { + m00 = in.readDouble(); + m01 = in.readDouble(); + m10 = in.readDouble(); + m11 = in.readDouble(); + } + + /** + * Apply rotation about the origin to this matrix by rotating the given amount of radians. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param angle + * the angle in radians + * @return this + */ + public Matrix2d rotate(double angle) { + return rotate(angle, this); + } + + public Matrix2d rotate(double angle, Matrix2d dest) { + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + // rotation matrix elements: + // m00 = c, m01 = s, m10 = -s, m11 = c + double nm00 = m00 * c + m10 * s; + double nm01 = m01 * c + m11 * s; + double nm10 = m10 * c - m00 * s; + double nm11 = m11 * c - m01 * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the origin. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double) + * + * @param angle + * the angle in radians to rotate about the X axis + * @return this + */ + public Matrix2d rotateLocal(double angle) { + return rotateLocal(angle, this); + } + + public Matrix2d rotateLocal(double angle, Matrix2d dest) { + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + // rotation matrix elements: + // m00 = c, m01 = s, m10 = -s, m11 = c + double nm00 = c * m00 - s * m01; + double nm01 = s * m00 + c * m01; + double nm10 = c * m10 - s * m11; + double nm11 = s * m10 + c * m11; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + public Vector2d getRow(int row, Vector2d dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + dest.x = m00; + dest.y = m10; + break; + case 1: + dest.x = m01; + dest.y = m11; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..1] + * @param src + * the row components to set + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..1] + */ + public Matrix2d setRow(int row, Vector2dc src) throws IndexOutOfBoundsException { + return setRow(row, src.x(), src.y()); + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..1] + * @param x + * the first element in the row + * @param y + * the second element in the row + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..1] + */ + public Matrix2d setRow(int row, double x, double y) throws IndexOutOfBoundsException { + switch (row) { + case 0: + this.m00 = x; + this.m10 = y; + break; + case 1: + this.m01 = x; + this.m11 = y; + break; + default: + throw new IndexOutOfBoundsException(); + } + return this; + } + + public Vector2d getColumn(int column, Vector2d dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + dest.x = m00; + dest.y = m01; + break; + case 1: + dest.x = m10; + dest.y = m11; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..1] + * @param src + * the column components to set + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..1] + */ + public Matrix2d setColumn(int column, Vector2dc src) throws IndexOutOfBoundsException { + return setColumn(column, src.x(), src.y()); + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..1] + * @param x + * the first element in the column + * @param y + * the second element in the column + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..1] + */ + public Matrix2d setColumn(int column, double x, double y) throws IndexOutOfBoundsException { + switch (column) { + case 0: + this.m00 = x; + this.m01 = y; + break; + case 1: + this.m10 = x; + this.m11 = y; + break; + default: + throw new IndexOutOfBoundsException(); + } + return this; + } + + public double get(int column, int row) { + switch (column) { + case 0: + switch (row) { + case 0: + return m00; + case 1: + return m01; + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m10; + case 1: + return m11; + default: + break; + } + break; + default: + break; + } + throw new IndexOutOfBoundsException(); + } + + /** + * Set the matrix element at the given column and row to the specified value. + * + * @param column + * the colum index in [0..1] + * @param row + * the row index in [0..1] + * @param value + * the value + * @return this + */ + public Matrix2d set(int column, int row, double value) { + switch (column) { + case 0: + switch (row) { + case 0: + this.m00 = value; + return this; + case 1: + this.m01 = value; + return this; + default: + break; + } + break; + case 1: + switch (row) { + case 0: + this.m10 = value; + return this; + case 1: + this.m11 = value; + return this; + default: + break; + } + break; + default: + break; + } + throw new IndexOutOfBoundsException(); + } + + /** + * Set this matrix to its own normal matrix. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In this case, use {@link #set(Matrix2dc)} to set a given Matrix2d to this matrix. + * + * @see #set(Matrix2dc) + * + * @return this + */ + public Matrix2d normal() { + return normal(this); + } + + /** + * Compute a normal matrix from this matrix and store it into dest. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In this case, use {@link #set(Matrix2dc)} to set a given Matrix2d to this matrix. + * + * @see #set(Matrix2dc) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix2d normal(Matrix2d dest) { + double det = m00 * m11 - m10 * m01; + double s = 1.0 / det; + /* Invert and transpose in one go */ + double nm00 = m11 * s; + double nm01 = -m10 * s; + double nm10 = -m01 * s; + double nm11 = m00 * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + public Vector2d getScale(Vector2d dest) { + dest.x = Math.sqrt(m00 * m00 + m01 * m01); + dest.y = Math.sqrt(m10 * m10 + m11 * m11); + return dest; + } + + public Vector2d positiveX(Vector2d dir) { + if (m00 * m11 < m01 * m10) { // negative determinant? + dir.x = -m11; + dir.y = m01; + } else { + dir.x = m11; + dir.y = -m01; + } + return dir.normalize(dir); + } + + public Vector2d normalizedPositiveX(Vector2d dir) { + if (m00 * m11 < m01 * m10) { // negative determinant? + dir.x = -m11; + dir.y = m01; + } else { + dir.x = m11; + dir.y = -m01; + } + return dir; + } + + public Vector2d positiveY(Vector2d dir) { + if (m00 * m11 < m01 * m10) { // negative determinant? + dir.x = m10; + dir.y = -m00; + } else { + dir.x = -m10; + dir.y = m00; + } + return dir.normalize(dir); + } + + public Vector2d normalizedPositiveY(Vector2d dir) { + if (m00 * m11 < m01 * m10) { // negative determinant? + dir.x = m10; + dir.y = -m00; + } else { + dir.x = -m10; + dir.y = m00; + } + return dir; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(m00); + result = prime * result + (int) ((temp >>> 32) ^ temp); + temp = Double.doubleToLongBits(m01); + result = prime * result + (int) ((temp >>> 32) ^ temp); + temp = Double.doubleToLongBits(m10); + result = prime * result + (int) ((temp >>> 32) ^ temp); + temp = Double.doubleToLongBits(m11); + result = prime * result + (int) ((temp >>> 32) ^ temp); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Matrix2d other = (Matrix2d) obj; + if (Double.doubleToLongBits(m00) != Double.doubleToLongBits(other.m00)) + return false; + if (Double.doubleToLongBits(m01) != Double.doubleToLongBits(other.m01)) + return false; + if (Double.doubleToLongBits(m10) != Double.doubleToLongBits(other.m10)) + return false; + if (Double.doubleToLongBits(m11) != Double.doubleToLongBits(other.m11)) + return false; + return true; + } + + public boolean equals(Matrix2dc m, double delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix2d)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + return true; + } + + /** + * Exchange the values of this matrix with the given other matrix. + * + * @param other + * the other matrix to exchange the values with + * @return this + */ + public Matrix2d swap(Matrix2d other) { + MemUtil.INSTANCE.swap(this, other); + return this; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix2d add(Matrix2dc other) { + return add(other, this); + } + + public Matrix2d add(Matrix2dc other, Matrix2d dest) { + dest.m00 = m00 + other.m00(); + dest.m01 = m01 + other.m01(); + dest.m10 = m10 + other.m10(); + dest.m11 = m11 + other.m11(); + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix2d sub(Matrix2dc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix2d sub(Matrix2dc other, Matrix2d dest) { + dest.m00 = m00 - other.m00(); + dest.m01 = m01 - other.m01(); + dest.m10 = m10 - other.m10(); + dest.m11 = m11 - other.m11(); + return dest; + } + + /** + * Component-wise multiply this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix2d mulComponentWise(Matrix2dc other) { + return sub(other, this); + } + + public Matrix2d mulComponentWise(Matrix2dc other, Matrix2d dest) { + dest.m00 = m00 * other.m00(); + dest.m01 = m01 * other.m01(); + dest.m10 = m10 * other.m10(); + dest.m11 = m11 * other.m11(); + return dest; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Matrix2d lerp(Matrix2dc other, double t) { + return lerp(other, t, this); + } + + public Matrix2d lerp(Matrix2dc other, double t, Matrix2d dest) { + dest.m00 = Math.fma(other.m00() - m00, t, m00); + dest.m01 = Math.fma(other.m01() - m01, t, m01); + dest.m10 = Math.fma(other.m10() - m10, t, m10); + dest.m11 = Math.fma(other.m11() - m11, t, m11); + return dest; + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && + Math.isFinite(m10) && Math.isFinite(m11); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2dc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2dc.java new file mode 100644 index 000000000..f1888a0b6 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2dc.java @@ -0,0 +1,725 @@ +/* + * The MIT License + * + * Copyright (c) 2020-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 2x2 matrix of double-precision floats. + * + * @author Joseph Burton + */ +public interface Matrix2dc { + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + double m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + double m01(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + double m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + double m11(); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix2d mul(Matrix2dc right, Matrix2d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix2d mul(Matrix2fc right, Matrix2d dest); + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in dest. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix2d mulLocal(Matrix2dc left, Matrix2d dest); + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + double determinant(); + + /** + * Invert the this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix2d invert(Matrix2d dest); + + /** + * Transpose this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix2d transpose(Matrix2d dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix2d get(Matrix2d dest); + + /** + * Get the current values of this matrix and store them as + * the rotational component of dest. All other values of dest will + * be set to 0. + * + * @see Matrix3x2d#set(Matrix2dc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3x2d get(Matrix3x2d dest); + + /** + * Get the current values of this matrix and store them as + * the rotational component of dest. All other values of dest will + * be set to identity. + * + * @see Matrix3d#set(Matrix2dc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3d get(Matrix3d dest); + + /** + * Get the angle of the rotation component of this matrix. + *

+ * This method assumes that there is a valid rotation to be returned, i.e. that + * atan2(-m10, m00) == atan2(m01, m11). + * + * @return the angle + */ + double getRotation(); + + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get(DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer get(int index, DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #getTransposed(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer getTransposed(DoubleBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer getTransposed(int index, DoubleBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getTransposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getTransposed(ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer getTransposed(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix2dc getToAddress(long address); + + /** + * Store this matrix into the supplied double array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] get(double[] arr, int offset); + + /** + * Store this matrix into the supplied double array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(double[], int)}. + * + * @see #get(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] get(double[] arr); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy.x and + * xy.y factors, respectively and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix2d scale(Vector2dc xy, Matrix2d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given x and + * y factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix2d scale(double x, double y, Matrix2d dest); + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xy factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(double, double, Matrix2d) + * + * @param xy + * the factor for all components + * @param dest + * will hold the result + * @return dest + */ + Matrix2d scale(double xy, Matrix2d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x and + * y factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix2d scaleLocal(double x, double y, Matrix2d dest); + + /** + * Transform the given vector by this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector2d transform(Vector2d v); + + /** + * Transform the given vector by this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2d transform(Vector2dc v, Vector2d dest); + + /** + * Transform the vector (x, y) by this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2d transform(double x, double y, Vector2d dest); + + /** + * Transform the given vector by the transpose of this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector2d transformTranspose(Vector2d v); + + /** + * Transform the given vector by the transpose of this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2d transformTranspose(Vector2dc v, Vector2d dest); + + /** + * Transform the vector (x, y) by the transpose of this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2d transformTranspose(double x, double y, Vector2d dest); + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix2d rotate(double ang, Matrix2d dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix2d rotateLocal(double ang, Matrix2d dest); + + /** + * Get the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..1] + * @param dest + * will hold the row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..1] + */ + Vector2d getRow(int row, Vector2d dest) throws IndexOutOfBoundsException; + + /** + * Get the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..1] + * @param dest + * will hold the column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..1] + */ + Vector2d getColumn(int column, Vector2d dest) throws IndexOutOfBoundsException; + + /** + * Get the matrix element value at the given column and row. + * + * @param column + * the colum index in [0..1] + * @param row + * the row index in [0..1] + * @return the element value + */ + double get(int column, int row); + + /** + * Compute a normal matrix from this matrix and store it into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix2d normal(Matrix2d dest); + + /** + * Get the scaling factors of this matrix for the three base axes. + * + * @param dest + * will hold the scaling factors for x and y + * @return dest + */ + Vector2d getScale(Vector2d dest); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix2d inv = new Matrix2d(this).invert();
+     * inv.transform(dir.set(1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector2d)} instead. + * + * @param dest + * will hold the direction of +X + * @return dest + */ + Vector2d positiveX(Vector2d dest); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix2d inv = new Matrix2d(this).transpose();
+     * inv.transform(dir.set(1, 0));
+     * 
+ * + * @param dest + * will hold the direction of +X + * @return dest + */ + Vector2d normalizedPositiveX(Vector2d dest); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix2d inv = new Matrix2d(this).invert();
+     * inv.transform(dir.set(0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector2d)} instead. + * + * @param dest + * will hold the direction of +Y + * @return dest + */ + Vector2d positiveY(Vector2d dest); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix2d inv = new Matrix2d(this).transpose();
+     * inv.transform(dir.set(0, 1));
+     * 
+ * + * @param dest + * will hold the direction of +Y + * @return dest + */ + Vector2d normalizedPositiveY(Vector2d dest); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix2d add(Matrix2dc other, Matrix2d dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix2d sub(Matrix2dc subtrahend, Matrix2d dest); + + /** + * Component-wise multiply this by other and store the result in dest. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix2d mulComponentWise(Matrix2dc other, Matrix2d dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Matrix2d lerp(Matrix2dc other, double t, Matrix2d dest); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix2dc m, double delta); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2f.java new file mode 100644 index 000000000..46e3622c0 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2f.java @@ -0,0 +1,1429 @@ +/* + * The MIT License + * + * Copyright (c) 2020-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a 2x2 matrix of floats, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10
+ * m01 m11
+ * + * @author Joseph Burton + */ +public class Matrix2f implements Externalizable, Cloneable, Matrix2fc { + + private static final long serialVersionUID = 1L; + + public float m00, m01; + public float m10, m11; + + /** + * Create a new {@link Matrix2f} and set it to {@link #identity() identity}. + */ + public Matrix2f() { + m00 = 1.0f; + m11 = 1.0f; + } + + /** + * Create a new {@link Matrix2f} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix2fc} to copy the values from + */ + public Matrix2f(Matrix2fc mat) { + if (mat instanceof Matrix2f) { + MemUtil.INSTANCE.copy((Matrix2f) mat, this); + } else { + setMatrix2fc(mat); + } + } + + /** + * Create a new {@link Matrix2f} and make it a copy of the upper left 2x2 of the given {@link Matrix3fc}. + * + * @param mat + * the {@link Matrix3fc} to copy the values from + */ + public Matrix2f(Matrix3fc mat) { + if (mat instanceof Matrix3f) { + MemUtil.INSTANCE.copy((Matrix3f) mat, this); + } else { + setMatrix3fc(mat); + } + } + + /** + * Create a new 2x2 matrix using the supplied float values. The order of the parameter is column-major, + * so the first two parameters specify the two elements of the first column. + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + */ + public Matrix2f(float m00, float m01, + float m10, float m11) { + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + } + + /** + * Create a new {@link Matrix2f} by reading its 4 float components from the given {@link FloatBuffer} + * at the buffer's current position. + *

+ * That FloatBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link FloatBuffer} to read the matrix values from + */ + public Matrix2f(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Matrix2f} and initialize its two columns using the supplied vectors. + * + * @param col0 + * the first column + * @param col1 + * the second column + */ + public Matrix2f(Vector2fc col0, Vector2fc col1) { + m00 = col0.x(); + m01 = col0.y(); + m10 = col1.x(); + m11 = col1.y(); + } + + public float m00() { + return m00; + } + public float m01() { + return m01; + } + public float m10() { + return m10; + } + public float m11() { + return m11; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + public Matrix2f m00(float m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + public Matrix2f m01(float m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + public Matrix2f m10(float m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + public Matrix2f m11(float m11) { + this.m11 = m11; + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + Matrix2f _m00(float m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + Matrix2f _m01(float m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + Matrix2f _m10(float m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + Matrix2f _m11(float m11) { + this.m11 = m11; + return this; + } + + /** + * Set the elements of this matrix to the ones in m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix2f set(Matrix2fc m) { + if (m instanceof Matrix2f) { + MemUtil.INSTANCE.copy((Matrix2f) m, this); + } else { + setMatrix2fc(m); + } + return this; + } + private void setMatrix2fc(Matrix2fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Set the elements of this matrix to the left 2x2 submatrix of m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix2f set(Matrix3x2fc m) { + if (m instanceof Matrix3x2f) { + MemUtil.INSTANCE.copy((Matrix3x2f) m, this); + } else { + setMatrix3x2fc(m); + } + return this; + } + private void setMatrix3x2fc(Matrix3x2fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Set the elements of this matrix to the upper left 2x2 of the given {@link Matrix3fc}. + * + * @param m + * the {@link Matrix3fc} to copy the values from + * @return this + */ + public Matrix2f set(Matrix3fc m) { + if (m instanceof Matrix3f) { + MemUtil.INSTANCE.copy((Matrix3f) m, this); + } else { + setMatrix3fc(m); + } + return this; + } + private void setMatrix3fc(Matrix3fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix2f mul(Matrix2fc right) { + return mul(right, this); + } + + public Matrix2f mul(Matrix2fc right, Matrix2f dest) { + float nm00 = m00 * right.m00() + m10 * right.m01(); + float nm01 = m01 * right.m00() + m11 * right.m01(); + float nm10 = m00 * right.m10() + m10 * right.m11(); + float nm11 = m01 * right.m10() + m11 * right.m11(); + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in this. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @return this + */ + public Matrix2f mulLocal(Matrix2fc left) { + return mulLocal(left, this); + } + + public Matrix2f mulLocal(Matrix2fc left, Matrix2f dest) { + float nm00 = left.m00() * m00 + left.m10() * m01; + float nm01 = left.m01() * m00 + left.m11() * m01; + float nm10 = left.m00() * m10 + left.m10() * m11; + float nm11 = left.m01() * m10 + left.m11() * m11; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Set the values within this matrix to the supplied float values. The result looks like this: + *

+ * m00, m10
+ * m01, m11
+ * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @return this + */ + public Matrix2f set(float m00, float m01, + float m10, float m11) { + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + return this; + } + + /** + * Set the values in this matrix based on the supplied float array. The result looks like this: + *

+ * 0, 2
+ * 1, 3
+ * + * This method only uses the first 4 values, all others are ignored. + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix2f set(float m[]) { + MemUtil.INSTANCE.copy(m, 0, this); + return this; + } + + /** + * Set the two columns of this matrix to the supplied vectors, respectively. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @return this + */ + public Matrix2f set(Vector2fc col0, Vector2fc col1) { + m00 = col0.x(); + m01 = col0.y(); + m10 = col1.x(); + m11 = col1.y(); + return this; + } + + public float determinant() { + return m00 * m11 - m10 * m01; + } + + /** + * Invert this matrix. + * + * @return this + */ + public Matrix2f invert() { + return invert(this); + } + + public Matrix2f invert(Matrix2f dest) { + float s = 1.0f / determinant(); + float nm00 = m11 * s; + float nm01 = -m01 * s; + float nm10 = -m10 * s; + float nm11 = m00 * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Transpose this matrix. + * + * @return this + */ + public Matrix2f transpose() { + return transpose(this); + } + + public Matrix2f transpose(Matrix2f dest) { + dest.set(m00, m10, + m01, m11); + return dest; + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix2fc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix2fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix2f get(Matrix2f dest) { + return dest.set(this); + } + + public Matrix3x2f get(Matrix3x2f dest) { + return dest.set(this); + } + + public Matrix3f get(Matrix3f dest) { + return dest.set(this); + } + + public float getRotation() { + return Math.atan2(m01, m11); + } + + + public FloatBuffer get(FloatBuffer buffer) { + return get(buffer.position(), buffer); + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public FloatBuffer getTransposed(FloatBuffer buffer) { + return get(buffer.position(), buffer); + } + + public FloatBuffer getTransposed(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public ByteBuffer getTransposed(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + public ByteBuffer getTransposed(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + public Matrix2fc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + public float[] get(float[] arr, int offset) { + MemUtil.INSTANCE.copy(this, arr, offset); + return arr; + } + + public float[] get(float[] arr) { + return get(arr, 0); + } + + /** + * Set the values of this matrix by reading 4 float values from the given {@link FloatBuffer} in column-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix2f set(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 4 float values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix2f set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 4 float values from the given {@link FloatBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix2f set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 4 float values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix2f set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 4 float values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix2f setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set all values within this matrix to zero. + * + * @return this + */ + public Matrix2f zero() { + MemUtil.INSTANCE.zero(this); + return this; + } + + /** + * Set this matrix to the identity. + * + * @return this + */ + public Matrix2f identity() { + MemUtil.INSTANCE.identity(this); + return this; + } + + public Matrix2f scale(Vector2fc xy, Matrix2f dest) { + return scale(xy.x(), xy.y(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy.x and + * xy.y factors, respectively. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @return this + */ + public Matrix2f scale(Vector2fc xy) { + return scale(xy.x(), xy.y(), this); + } + + public Matrix2f scale(float x, float y, Matrix2f dest) { + // scale matrix elements: + // m00 = x, m11 = y + // all others = 0 + dest.m00 = m00 * x; + dest.m01 = m01 * x; + dest.m10 = m10 * y; + dest.m11 = m11 * y; + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x and + * y factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix2f scale(float x, float y) { + return scale(x, y, this); + } + + public Matrix2f scale(float xy, Matrix2f dest) { + return scale(xy, xy, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xy factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(float, float) + * + * @param xy + * the factor for all components + * @return this + */ + public Matrix2f scale(float xy) { + return scale(xy, xy); + } + + public Matrix2f scaleLocal(float x, float y, Matrix2f dest) { + dest.m00 = x * m00; + dest.m01 = y * m01; + dest.m10 = x * m10; + dest.m11 = y * m11; + return dest; + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x and + * y factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix2f scaleLocal(float x, float y) { + return scaleLocal(x, y, this); + } + + /** + * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(float) scale()} instead. + * + * @see #scale(float) + * + * @param factor + * the scale factor in x and y + * @return this + */ + public Matrix2f scaling(float factor) { + MemUtil.INSTANCE.zero(this); + m00 = factor; + m11 = factor; + return this; + } + + /** + * Set this matrix to be a simple scale matrix. + * + * @param x + * the scale in x + * @param y + * the scale in y + * @return this + */ + public Matrix2f scaling(float x, float y) { + MemUtil.INSTANCE.zero(this); + m00 = x; + m11 = y; + return this; + } + + /** + * Set this matrix to be a simple scale matrix which scales the base axes by xy.x and xy.y respectively. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix use {@link #scale(Vector2fc) scale()} instead. + * + * @see #scale(Vector2fc) + * + * @param xy + * the scale in x and y respectively + * @return this + */ + public Matrix2f scaling(Vector2fc xy) { + return scaling(xy.x(), xy.y()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about the origin. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to post-multiply a rotation transformation directly to a + * matrix, use {@link #rotate(float) rotate()} instead. + * + * @see #rotate(float) + * + * @param angle + * the angle in radians + * @return this + */ + public Matrix2f rotation(float angle) { + float sin = Math.sin(angle); + float cos = Math.cosFromSin(sin, angle); + m00 = cos; + m01 = sin; + m10 = -sin; + m11 = cos; + return this; + } + + public Vector2f transform(Vector2f v) { + return v.mul(this); + } + + public Vector2f transform(Vector2fc v, Vector2f dest) { + v.mul(this, dest); + return dest; + } + + public Vector2f transform(float x, float y, Vector2f dest) { + dest.set(m00 * x + m10 * y, + m01 * x + m11 * y); + return dest; + } + + public Vector2f transformTranspose(Vector2f v) { + return v.mulTranspose(this); + } + + public Vector2f transformTranspose(Vector2fc v, Vector2f dest) { + v.mulTranspose(this, dest); + return dest; + } + + public Vector2f transformTranspose(float x, float y, Vector2f dest) { + dest.set(m00 * x + m01 * y, + m10 * x + m11 * y); + return dest; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(m00); + out.writeFloat(m01); + out.writeFloat(m10); + out.writeFloat(m11); + } + + public void readExternal(ObjectInput in) throws IOException { + m00 = in.readFloat(); + m01 = in.readFloat(); + m10 = in.readFloat(); + m11 = in.readFloat(); + } + + /** + * Apply rotation about the origin to this matrix by rotating the given amount of radians. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param angle + * the angle in radians + * @return this + */ + public Matrix2f rotate(float angle) { + return rotate(angle, this); + } + + public Matrix2f rotate(float angle, Matrix2f dest) { + float s = Math.sin(angle); + float c = Math.cosFromSin(s, angle); + // rotation matrix elements: + // m00 = c, m01 = s, m10 = -s, m11 = c + float nm00 = m00 * c + m10 * s; + float nm01 = m01 * c + m11 * s; + float nm10 = m10 * c - m00 * s; + float nm11 = m11 * c - m01 * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the origin. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float) + * + * @param angle + * the angle in radians to rotate about the X axis + * @return this + */ + public Matrix2f rotateLocal(float angle) { + return rotateLocal(angle, this); + } + + public Matrix2f rotateLocal(float angle, Matrix2f dest) { + float s = Math.sin(angle); + float c = Math.cosFromSin(s, angle); + // rotation matrix elements: + // m00 = c, m01 = s, m10 = -s, m11 = c + float nm00 = c * m00 - s * m01; + float nm01 = s * m00 + c * m01; + float nm10 = c * m10 - s * m11; + float nm11 = s * m10 + c * m11; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + public Vector2f getRow(int row, Vector2f dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + dest.x = m00; + dest.y = m10; + break; + case 1: + dest.x = m01; + dest.y = m11; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..1] + * @param src + * the row components to set + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..1] + */ + public Matrix2f setRow(int row, Vector2fc src) throws IndexOutOfBoundsException { + return setRow(row, src.x(), src.y()); + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..1] + * @param x + * the first element in the row + * @param y + * the second element in the row + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..1] + */ + public Matrix2f setRow(int row, float x, float y) throws IndexOutOfBoundsException { + switch (row) { + case 0: + this.m00 = x; + this.m10 = y; + break; + case 1: + this.m01 = x; + this.m11 = y; + break; + default: + throw new IndexOutOfBoundsException(); + } + return this; + } + + public Vector2f getColumn(int column, Vector2f dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + dest.x = m00; + dest.y = m01; + break; + case 1: + dest.x = m10; + dest.y = m11; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..1] + * @param src + * the column components to set + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..1] + */ + public Matrix2f setColumn(int column, Vector2fc src) throws IndexOutOfBoundsException { + return setColumn(column, src.x(), src.y()); + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..1] + * @param x + * the first element in the column + * @param y + * the second element in the column + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..1] + */ + public Matrix2f setColumn(int column, float x, float y) throws IndexOutOfBoundsException { + switch (column) { + case 0: + this.m00 = x; + this.m01 = y; + break; + case 1: + this.m10 = x; + this.m11 = y; + break; + default: + throw new IndexOutOfBoundsException(); + } + return this; + } + + public float get(int column, int row) { + switch (column) { + case 0: + switch (row) { + case 0: + return m00; + case 1: + return m01; + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m10; + case 1: + return m11; + default: + break; + } + break; + default: + break; + } + throw new IndexOutOfBoundsException(); + } + + /** + * Set the matrix element at the given column and row to the specified value. + * + * @param column + * the colum index in [0..1] + * @param row + * the row index in [0..1] + * @param value + * the value + * @return this + */ + public Matrix2f set(int column, int row, float value) { + switch (column) { + case 0: + switch (row) { + case 0: + this.m00 = value; + return this; + case 1: + this.m01 = value; + return this; + default: + break; + } + break; + case 1: + switch (row) { + case 0: + this.m10 = value; + return this; + case 1: + this.m11 = value; + return this; + default: + break; + } + break; + default: + break; + } + throw new IndexOutOfBoundsException(); + } + + /** + * Set this matrix to its own normal matrix. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In this case, use {@link #set(Matrix2fc)} to set a given Matrix2f to this matrix. + * + * @see #set(Matrix2fc) + * + * @return this + */ + public Matrix2f normal() { + return normal(this); + } + + /** + * Compute a normal matrix from this matrix and store it into dest. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In this case, use {@link #set(Matrix2fc)} to set a given Matrix2f to this matrix. + * + * @see #set(Matrix2fc) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix2f normal(Matrix2f dest) { + float det = m00 * m11 - m10 * m01; + float s = 1.0f / det; + /* Invert and transpose in one go */ + float nm00 = m11 * s; + float nm01 = -m10 * s; + float nm10 = -m01 * s; + float nm11 = m00 * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + return dest; + } + + public Vector2f getScale(Vector2f dest) { + dest.x = Math.sqrt(m00 * m00 + m01 * m01); + dest.y = Math.sqrt(m10 * m10 + m11 * m11); + return dest; + } + + public Vector2f positiveX(Vector2f dir) { + if (m00 * m11 < m01 * m10) { // negative determinant? + dir.x = -m11; + dir.y = m01; + } else { + dir.x = m11; + dir.y = -m01; + } + return dir.normalize(dir); + } + + public Vector2f normalizedPositiveX(Vector2f dir) { + if (m00 * m11 < m01 * m10) { // negative determinant? + dir.x = -m11; + dir.y = m01; + } else { + dir.x = m11; + dir.y = -m01; + } + return dir; + } + + public Vector2f positiveY(Vector2f dir) { + if (m00 * m11 < m01 * m10) { // negative determinant? + dir.x = m10; + dir.y = -m00; + } else { + dir.x = -m10; + dir.y = m00; + } + return dir.normalize(dir); + } + + public Vector2f normalizedPositiveY(Vector2f dir) { + if (m00 * m11 < m01 * m10) { // negative determinant? + dir.x = m10; + dir.y = -m00; + } else { + dir.x = -m10; + dir.y = m00; + } + return dir; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(m00); + result = prime * result + Float.floatToIntBits(m01); + result = prime * result + Float.floatToIntBits(m10); + result = prime * result + Float.floatToIntBits(m11); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Matrix2f other = (Matrix2f) obj; + if (Float.floatToIntBits(m00) != Float.floatToIntBits(other.m00)) + return false; + if (Float.floatToIntBits(m01) != Float.floatToIntBits(other.m01)) + return false; + if (Float.floatToIntBits(m10) != Float.floatToIntBits(other.m10)) + return false; + if (Float.floatToIntBits(m11) != Float.floatToIntBits(other.m11)) + return false; + return true; + } + + public boolean equals(Matrix2fc m, float delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix2f)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + return true; + } + + /** + * Exchange the values of this matrix with the given other matrix. + * + * @param other + * the other matrix to exchange the values with + * @return this + */ + public Matrix2f swap(Matrix2f other) { + MemUtil.INSTANCE.swap(this, other); + return this; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix2f add(Matrix2fc other) { + return add(other, this); + } + + public Matrix2f add(Matrix2fc other, Matrix2f dest) { + dest.m00 = m00 + other.m00(); + dest.m01 = m01 + other.m01(); + dest.m10 = m10 + other.m10(); + dest.m11 = m11 + other.m11(); + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix2f sub(Matrix2fc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix2f sub(Matrix2fc other, Matrix2f dest) { + dest.m00 = m00 - other.m00(); + dest.m01 = m01 - other.m01(); + dest.m10 = m10 - other.m10(); + dest.m11 = m11 - other.m11(); + return dest; + } + + /** + * Component-wise multiply this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix2f mulComponentWise(Matrix2fc other) { + return sub(other, this); + } + + public Matrix2f mulComponentWise(Matrix2fc other, Matrix2f dest) { + dest.m00 = m00 * other.m00(); + dest.m01 = m01 * other.m01(); + dest.m10 = m10 * other.m10(); + dest.m11 = m11 * other.m11(); + return dest; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Matrix2f lerp(Matrix2fc other, float t) { + return lerp(other, t, this); + } + + public Matrix2f lerp(Matrix2fc other, float t, Matrix2f dest) { + dest.m00 = Math.fma(other.m00() - m00, t, m00); + dest.m01 = Math.fma(other.m01() - m01, t, m01); + dest.m10 = Math.fma(other.m10() - m10, t, m10); + dest.m11 = Math.fma(other.m11() - m11, t, m11); + return dest; + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && + Math.isFinite(m10) && Math.isFinite(m11); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2fc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2fc.java new file mode 100644 index 000000000..3b8c21e9d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix2fc.java @@ -0,0 +1,709 @@ +/* + * The MIT License + * + * Copyright (c) 2020-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 2x2 matrix of single-precision floats. + * + * @author Joseph Burton + */ +public interface Matrix2fc { + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + float m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + float m01(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + float m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + float m11(); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix2f mul(Matrix2fc right, Matrix2f dest); + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in dest. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix2f mulLocal(Matrix2fc left, Matrix2f dest); + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + float determinant(); + + /** + * Invert the this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix2f invert(Matrix2f dest); + + /** + * Transpose this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix2f transpose(Matrix2f dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix2f get(Matrix2f dest); + + /** + * Get the current values of this matrix and store them as + * the rotational component of dest. All other values of dest will + * be set to 0. + * + * @see Matrix3x2f#set(Matrix2fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3x2f get(Matrix3x2f dest); + + /** + * Get the current values of this matrix and store them as + * the rotational component of dest. All other values of dest will + * be set to identity. + * + * @see Matrix3f#set(Matrix2fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3f get(Matrix3f dest); + + /** + * Get the angle of the rotation component of this matrix. + *

+ * This method assumes that there is a valid rotation to be returned, i.e. that + * atan2(-m10, m00) == atan2(m01, m11). + * + * @return the angle + */ + float getRotation(); + + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #getTransposed(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer getTransposed(FloatBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer getTransposed(int index, FloatBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getTransposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getTransposed(ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer getTransposed(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix2fc getToAddress(long address); + + /** + * Store this matrix into the supplied float array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get(float[] arr, int offset); + + /** + * Store this matrix into the supplied float array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get(float[] arr); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy.x and + * xy.y factors, respectively and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix2f scale(Vector2fc xy, Matrix2f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given x and + * y factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix2f scale(float x, float y, Matrix2f dest); + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xy factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(float, float, Matrix2f) + * + * @param xy + * the factor for all components + * @param dest + * will hold the result + * @return dest + */ + Matrix2f scale(float xy, Matrix2f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x and + * y factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix2f scaleLocal(float x, float y, Matrix2f dest); + + /** + * Transform the given vector by this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector2f transform(Vector2f v); + + /** + * Transform the given vector by this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2f transform(Vector2fc v, Vector2f dest); + + /** + * Transform the vector (x, y) by this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2f transform(float x, float y, Vector2f dest); + + /** + * Transform the given vector by the transpose of this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector2f transformTranspose(Vector2f v); + + /** + * Transform the given vector by the transpose of this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2f transformTranspose(Vector2fc v, Vector2f dest); + + /** + * Transform the vector (x, y) by the transpose of this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2f transformTranspose(float x, float y, Vector2f dest); + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix2f rotate(float ang, Matrix2f dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * The produced rotation will rotate a vector counter-clockwise around the origin. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix2f rotateLocal(float ang, Matrix2f dest); + + /** + * Get the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..1] + * @param dest + * will hold the row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..1] + */ + Vector2f getRow(int row, Vector2f dest) throws IndexOutOfBoundsException; + + /** + * Get the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..1] + * @param dest + * will hold the column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..1] + */ + Vector2f getColumn(int column, Vector2f dest) throws IndexOutOfBoundsException; + + /** + * Get the matrix element value at the given column and row. + * + * @param column + * the colum index in [0..1] + * @param row + * the row index in [0..1] + * @return the element value + */ + float get(int column, int row); + + /** + * Compute a normal matrix from this matrix and store it into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix2f normal(Matrix2f dest); + + /** + * Get the scaling factors of this matrix for the three base axes. + * + * @param dest + * will hold the scaling factors for x and y + * @return dest + */ + Vector2f getScale(Vector2f dest); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix2f inv = new Matrix2f(this).invert();
+     * inv.transform(dir.set(1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector2f)} instead. + * + * @param dest + * will hold the direction of +X + * @return dest + */ + Vector2f positiveX(Vector2f dest); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix2f inv = new Matrix2f(this).transpose();
+     * inv.transform(dir.set(1, 0));
+     * 
+ * + * @param dest + * will hold the direction of +X + * @return dest + */ + Vector2f normalizedPositiveX(Vector2f dest); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix2f inv = new Matrix2f(this).invert();
+     * inv.transform(dir.set(0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector2f)} instead. + * + * @param dest + * will hold the direction of +Y + * @return dest + */ + Vector2f positiveY(Vector2f dest); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix2f inv = new Matrix2f(this).transpose();
+     * inv.transform(dir.set(0, 1));
+     * 
+ * + * @param dest + * will hold the direction of +Y + * @return dest + */ + Vector2f normalizedPositiveY(Vector2f dest); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix2f add(Matrix2fc other, Matrix2f dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix2f sub(Matrix2fc subtrahend, Matrix2f dest); + + /** + * Component-wise multiply this by other and store the result in dest. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix2f mulComponentWise(Matrix2fc other, Matrix2f dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Matrix2f lerp(Matrix2fc other, float t, Matrix2f dest); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix2fc m, float delta); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3d.java new file mode 100644 index 000000000..97fddc563 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3d.java @@ -0,0 +1,5582 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a 3x3 matrix of doubles, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10 m20
+ * m01 m11 m21
+ * m02 m12 m22
+ * + * @author Richard Greenlees + * @author Kai Burjack + */ +public class Matrix3d implements Externalizable, Cloneable, Matrix3dc { + + private static final long serialVersionUID = 1L; + + public double m00, m01, m02; + public double m10, m11, m12; + public double m20, m21, m22; + + /** + * Create a new {@link Matrix3d} and initialize it to {@link #identity() identity}. + */ + public Matrix3d() { + m00 = 1.0; + m11 = 1.0; + m22 = 1.0; + } + + /** + * Create a new {@link Matrix3d} by setting its uppper left 2x2 submatrix to the values of the given {@link Matrix2dc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix2dc} + */ + public Matrix3d(Matrix2dc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix3d} by setting its uppper left 2x2 submatrix to the values of the given {@link Matrix2fc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix2fc} + */ + public Matrix3d(Matrix2fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix3d} and initialize it with the values from the given matrix. + * + * @param mat + * the matrix to initialize this matrix with + */ + public Matrix3d(Matrix3dc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix3d} and initialize it with the values from the given matrix. + * + * @param mat + * the matrix to initialize this matrix with + */ + public Matrix3d(Matrix3fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix3d} and make it a copy of the upper left 3x3 of the given {@link Matrix4fc}. + * + * @param mat + * the {@link Matrix4fc} to copy the values from + */ + public Matrix3d(Matrix4fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix3d} and make it a copy of the upper left 3x3 of the given {@link Matrix4dc}. + * + * @param mat + * the {@link Matrix4dc} to copy the values from + */ + public Matrix3d(Matrix4dc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix3d} and initialize its elements with the given values. + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m02 + * the value of m02 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + * @param m12 + * the value of m12 + * @param m20 + * the value of m20 + * @param m21 + * the value of m21 + * @param m22 + * the value of m22 + */ + public Matrix3d(double m00, double m01, double m02, + double m10, double m11, double m12, + double m20, double m21, double m22) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + } + + /** + * Create a new {@link Matrix3d} by reading its 9 double components from the given {@link DoubleBuffer} + * at the buffer's current position. + *

+ * That DoubleBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link DoubleBuffer} to read the matrix values from + */ + public Matrix3d(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Matrix3d} and initialize its three columns using the supplied vectors. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + */ + public Matrix3d(Vector3dc col0, Vector3dc col1, Vector3dc col2) { + set(col0, col1, col2); + } + + public double m00() { + return m00; + } + public double m01() { + return m01; + } + public double m02() { + return m02; + } + public double m10() { + return m10; + } + public double m11() { + return m11; + } + public double m12() { + return m12; + } + public double m20() { + return m20; + } + public double m21() { + return m21; + } + public double m22() { + return m22; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + public Matrix3d m00(double m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + public Matrix3d m01(double m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2. + * + * @param m02 + * the new value + * @return this + */ + public Matrix3d m02(double m02) { + this.m02 = m02; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + public Matrix3d m10(double m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + public Matrix3d m11(double m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2. + * + * @param m12 + * the new value + * @return this + */ + public Matrix3d m12(double m12) { + this.m12 = m12; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + public Matrix3d m20(double m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + public Matrix3d m21(double m21) { + this.m21 = m21; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2. + * + * @param m22 + * the new value + * @return this + */ + public Matrix3d m22(double m22) { + this.m22 = m22; + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + Matrix3d _m00(double m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + Matrix3d _m01(double m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2. + * + * @param m02 + * the new value + * @return this + */ + Matrix3d _m02(double m02) { + this.m02 = m02; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + Matrix3d _m10(double m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + Matrix3d _m11(double m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2. + * + * @param m12 + * the new value + * @return this + */ + Matrix3d _m12(double m12) { + this.m12 = m12; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + Matrix3d _m20(double m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + Matrix3d _m21(double m21) { + this.m21 = m21; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2. + * + * @param m22 + * the new value + * @return this + */ + Matrix3d _m22(double m22) { + this.m22 = m22; + return this; + } + + /** + * Set the values in this matrix to the ones in m. + * + * @param m + * the matrix whose values will be copied + * @return this + */ + public Matrix3d set(Matrix3dc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + return this; + } + + /** + * Store the values of the transpose of the given matrix m into this matrix. + * + * @param m + * the matrix to copy the transposed values from + * @return this + */ + public Matrix3d setTransposed(Matrix3dc m) { + double nm10 = m.m01(), nm12 = m.m21(); + double nm20 = m.m02(), nm21 = m.m12(); + return this + ._m00(m.m00())._m01(m.m10())._m02(m.m20()) + ._m10(nm10)._m11(m.m11())._m12(nm12) + ._m20(nm20)._m21(nm21)._m22(m.m22()); + } + + /** + * Set the values in this matrix to the ones in m. + * + * @param m + * the matrix whose values will be copied + * @return this + */ + public Matrix3d set(Matrix3fc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + return this; + } + + /** + * Store the values of the transpose of the given matrix m into this matrix. + * + * @param m + * the matrix to copy the transposed values from + * @return this + */ + public Matrix3d setTransposed(Matrix3fc m) { + float nm10 = m.m01(), nm12 = m.m21(); + float nm20 = m.m02(), nm21 = m.m12(); + return this + ._m00(m.m00())._m01(m.m10())._m02(m.m20()) + ._m10(nm10)._m11(m.m11())._m12(nm12) + ._m20(nm20)._m21(nm21)._m22(m.m22()); + } + + /** + * Set the elements of this matrix to the left 3x3 submatrix of m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix3d set(Matrix4x3dc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + return this; + } + + /** + * Set the elements of this matrix to the upper left 3x3 of the given {@link Matrix4fc}. + * + * @param mat + * the {@link Matrix4fc} to copy the values from + * @return this + */ + public Matrix3d set(Matrix4fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + return this; + } + + /** + * Set the elements of this matrix to the upper left 3x3 of the given {@link Matrix4dc}. + * + * @param mat + * the {@link Matrix4dc} to copy the values from + * @return this + */ + public Matrix3d set(Matrix4dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + return this; + } + + /** + * Set the upper left 2x2 submatrix of this {@link Matrix3d} to the given {@link Matrix2fc} + * and the rest to identity. + * + * @see #Matrix3d(Matrix2fc) + * + * @param mat + * the {@link Matrix2fc} + * @return this + */ + public Matrix3d set(Matrix2fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = 0.0; + m10 = mat.m10(); + m11 = mat.m11(); + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + return this; + } + + /** + * Set the upper left 2x2 submatrix of this {@link Matrix3d} to the given {@link Matrix2dc} + * and the rest to identity. + * + * @see #Matrix3d(Matrix2dc) + * + * @param mat + * the {@link Matrix2dc} + * @return this + */ + public Matrix3d set(Matrix2dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = 0.0; + m10 = mat.m10(); + m11 = mat.m11(); + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + * @return this + */ + public Matrix3d set(AxisAngle4f axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double invLength = Math.invsqrt(x*x + y*y + z*z); + x *= invLength; + y *= invLength; + z *= invLength; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0 - c; + m00 = c + x*x*omc; + m11 = c + y*y*omc; + m22 = c + z*z*omc; + double tmp1 = x*y*omc; + double tmp2 = z*s; + m10 = tmp1 - tmp2; + m01 = tmp1 + tmp2; + tmp1 = x*z*omc; + tmp2 = y*s; + m20 = tmp1 + tmp2; + m02 = tmp1 - tmp2; + tmp1 = y*z*omc; + tmp2 = x*s; + m21 = tmp1 - tmp2; + m12 = tmp1 + tmp2; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + * @return this + */ + public Matrix3d set(AxisAngle4d axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double invLength = Math.invsqrt(x*x + y*y + z*z); + x *= invLength; + y *= invLength; + z *= invLength; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0 - c; + m00 = c + x*x*omc; + m11 = c + y*y*omc; + m22 = c + z*z*omc; + double tmp1 = x*y*omc; + double tmp2 = z*s; + m10 = tmp1 - tmp2; + m01 = tmp1 + tmp2; + tmp1 = x*z*omc; + tmp2 = y*s; + m20 = tmp1 + tmp2; + m02 = tmp1 - tmp2; + tmp1 = y*z*omc; + tmp2 = x*s; + m21 = tmp1 - tmp2; + m12 = tmp1 + tmp2; + return this; + } + + /** + * Set this matrix to a rotation - and possibly scaling - equivalent to the given quaternion. + *

+ * This method is equivalent to calling: rotation(q) + *

+ * Reference: http://www.euclideanspace.com/ + * + * @see #rotation(Quaternionfc) + * + * @param q + * the quaternion + * @return this + */ + public Matrix3d set(Quaternionfc q) { + return rotation(q); + } + + /** + * Set this matrix to a rotation - and possibly scaling - equivalent to the given quaternion. + *

+ * This method is equivalent to calling: rotation(q) + *

+ * Reference: http://www.euclideanspace.com/ + * + * @see #rotation(Quaterniondc) + * + * @param q + * the quaternion + * @return this + */ + public Matrix3d set(Quaterniondc q) { + return rotation(q); + } + + /** + * Multiply this matrix by the supplied matrix. + * This matrix will be the left one. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand + * @return this + */ + public Matrix3d mul(Matrix3dc right) { + return mul(right, this); + } + + public Matrix3d mul(Matrix3dc right, Matrix3d dest) { + double nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), m20 * right.m02())); + double nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), m21 * right.m02())); + double nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), m22 * right.m02())); + double nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), m20 * right.m12())); + double nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), m21 * right.m12())); + double nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), m22 * right.m12())); + double nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), m20 * right.m22())); + double nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), m21 * right.m22())); + double nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), m22 * right.m22())); + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in this. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @return this + */ + public Matrix3d mulLocal(Matrix3dc left) { + return mulLocal(left, this); + } + + public Matrix3d mulLocal(Matrix3dc left, Matrix3d dest) { + double nm00 = left.m00() * m00 + left.m10() * m01 + left.m20() * m02; + double nm01 = left.m01() * m00 + left.m11() * m01 + left.m21() * m02; + double nm02 = left.m02() * m00 + left.m12() * m01 + left.m22() * m02; + double nm10 = left.m00() * m10 + left.m10() * m11 + left.m20() * m12; + double nm11 = left.m01() * m10 + left.m11() * m11 + left.m21() * m12; + double nm12 = left.m02() * m10 + left.m12() * m11 + left.m22() * m12; + double nm20 = left.m00() * m20 + left.m10() * m21 + left.m20() * m22; + double nm21 = left.m01() * m20 + left.m11() * m21 + left.m21() * m22; + double nm22 = left.m02() * m20 + left.m12() * m21 + left.m22() * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Multiply this matrix by the supplied matrix. + * This matrix will be the left one. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand + * @return this + */ + public Matrix3d mul(Matrix3fc right) { + return mul(right, this); + } + + public Matrix3d mul(Matrix3fc right, Matrix3d dest) { + double nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), m20 * right.m02())); + double nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), m21 * right.m02())); + double nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), m22 * right.m02())); + double nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), m20 * right.m12())); + double nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), m21 * right.m12())); + double nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), m22 * right.m12())); + double nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), m20 * right.m22())); + double nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), m21 * right.m22())); + double nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), m22 * right.m22())); + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Set the values within this matrix to the supplied double values. The result looks like this: + *

+ * m00, m10, m20
+ * m01, m11, m21
+ * m02, m12, m22
+ * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m02 + * the new value of m02 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @param m12 + * the new value of m12 + * @param m20 + * the new value of m20 + * @param m21 + * the new value of m21 + * @param m22 + * the new value of m22 + * @return this + */ + public Matrix3d set(double m00, double m01, double m02, + double m10, double m11, double m12, + double m20, double m21, double m22) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + return this; + } + + /** + * Set the values in this matrix based on the supplied double array. The result looks like this: + *

+ * 0, 3, 6
+ * 1, 4, 7
+ * 2, 5, 8
+ *

+ * Only uses the first 9 values, all others are ignored. + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix3d set(double m[]) { + m00 = m[0]; + m01 = m[1]; + m02 = m[2]; + m10 = m[3]; + m11 = m[4]; + m12 = m[5]; + m20 = m[6]; + m21 = m[7]; + m22 = m[8]; + return this; + } + + /** + * Set the values in this matrix based on the supplied double array. The result looks like this: + *

+ * 0, 3, 6
+ * 1, 4, 7
+ * 2, 5, 8
+ *

+ * Only uses the first 9 values, all others are ignored + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix3d set(float m[]) { + m00 = m[0]; + m01 = m[1]; + m02 = m[2]; + m10 = m[3]; + m11 = m[4]; + m12 = m[5]; + m20 = m[6]; + m21 = m[7]; + m22 = m[8]; + return this; + } + + public double determinant() { + return (m00 * m11 - m01 * m10) * m22 + + (m02 * m10 - m00 * m12) * m21 + + (m01 * m12 - m02 * m11) * m20; + } + + /** + * Invert this matrix. + * + * @return this + */ + public Matrix3d invert() { + return invert(this); + } + + public Matrix3d invert(Matrix3d dest) { + double a = Math.fma(m00, m11, -m01 * m10); + double b = Math.fma(m02, m10, -m00 * m12); + double c = Math.fma(m01, m12, -m02 * m11); + double d = Math.fma(a, m22, Math.fma(b, m21, c * m20)); + double s = 1.0 / d; + double nm00 = Math.fma(m11, m22, -m21 * m12) * s; + double nm01 = Math.fma(m21, m02, -m01 * m22) * s; + double nm02 = c * s; + double nm10 = Math.fma(m20, m12, -m10 * m22) * s; + double nm11 = Math.fma(m00, m22, -m20 * m02) * s; + double nm12 = b * s; + double nm20 = Math.fma(m10, m21, -m20 * m11) * s; + double nm21 = Math.fma(m20, m01, -m00 * m21) * s; + double nm22 = a * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Transpose this matrix. + * + * @return this + */ + public Matrix3d transpose() { + return transpose(this); + } + + public Matrix3d transpose(Matrix3d dest) { + dest.set(m00, m10, m20, + m01, m11, m21, + m02, m12, m22); + return dest; + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + " " + Runtime.format(m20, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + " " + Runtime.format(m21, formatter) + "\n" + + Runtime.format(m02, formatter) + " " + Runtime.format(m12, formatter) + " " + Runtime.format(m22, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix3dc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix3dc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix3d get(Matrix3d dest) { + return dest.set(this); + } + + public AxisAngle4f getRotation(AxisAngle4f dest) { + return dest.set(this); + } + + public Quaternionf getUnnormalizedRotation(Quaternionf dest) { + return dest.setFromUnnormalized(this); + } + + public Quaternionf getNormalizedRotation(Quaternionf dest) { + return dest.setFromNormalized(this); + } + + public Quaterniond getUnnormalizedRotation(Quaterniond dest) { + return dest.setFromUnnormalized(this); + } + + public Quaterniond getNormalizedRotation(Quaterniond dest) { + return dest.setFromNormalized(this); + } + + public DoubleBuffer get(DoubleBuffer buffer) { + return get(buffer.position(), buffer); + } + + public DoubleBuffer get(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public FloatBuffer get(FloatBuffer buffer) { + return get(buffer.position(), buffer); + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.putf(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer getFloats(ByteBuffer buffer) { + return getFloats(buffer.position(), buffer); + } + + public ByteBuffer getFloats(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putf(this, index, buffer); + return buffer; + } + + public Matrix3dc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + public double[] get(double[] arr, int offset) { + arr[offset+0] = m00; + arr[offset+1] = m01; + arr[offset+2] = m02; + arr[offset+3] = m10; + arr[offset+4] = m11; + arr[offset+5] = m12; + arr[offset+6] = m20; + arr[offset+7] = m21; + arr[offset+8] = m22; + return arr; + } + + public double[] get(double[] arr) { + return get(arr, 0); + } + + public float[] get(float[] arr, int offset) { + arr[offset+0] = (float)m00; + arr[offset+1] = (float)m01; + arr[offset+2] = (float)m02; + arr[offset+3] = (float)m10; + arr[offset+4] = (float)m11; + arr[offset+5] = (float)m12; + arr[offset+6] = (float)m20; + arr[offset+7] = (float)m21; + arr[offset+8] = (float)m22; + return arr; + } + + public float[] get(float[] arr) { + return get(arr, 0); + } + + /** + * Set the values of this matrix by reading 9 double values from the given {@link DoubleBuffer} in column-major order, + * starting at its current position. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3d set(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 float values from the given {@link FloatBuffer} in column-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3d set(FloatBuffer buffer) { + MemUtil.INSTANCE.getf(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 double values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3d set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 float values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3d setFloats(ByteBuffer buffer) { + MemUtil.INSTANCE.getf(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 double values from the given {@link DoubleBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3d set(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 float values from the given {@link FloatBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3d set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.getf(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 double values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3d set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 float values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3d setFloats(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.getf(this, index, buffer); + return this; + } + /** + * Set the values of this matrix by reading 9 double values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix3d setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set the three columns of this matrix to the supplied vectors, respectively. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @return this + */ + public Matrix3d set(Vector3dc col0, + Vector3dc col1, + Vector3dc col2) { + this.m00 = col0.x(); + this.m01 = col0.y(); + this.m02 = col0.z(); + this.m10 = col1.x(); + this.m11 = col1.y(); + this.m12 = col1.z(); + this.m20 = col2.x(); + this.m21 = col2.y(); + this.m22 = col2.z(); + return this; + } + + /** + * Set all the values within this matrix to 0. + * + * @return this + */ + public Matrix3d zero() { + m00 = 0.0; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 0.0; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 0.0; + return this; + } + + /** + * Set this matrix to the identity. + * + * @return this + */ + public Matrix3d identity() { + m00 = 1.0; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 1.0; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + return this; + } + + /** + * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(double) scale()} instead. + * + * @see #scale(double) + * + * @param factor + * the scale factor in x, y and z + * @return this + */ + public Matrix3d scaling(double factor) { + m00 = factor; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = factor; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = factor; + return this; + } + + /** + * Set this matrix to be a simple scale matrix. + * + * @param x + * the scale in x + * @param y + * the scale in y + * @param z + * the scale in z + * @return this + */ + public Matrix3d scaling(double x, double y, double z) { + m00 = x; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = y; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = z; + return this; + } + + /** + * Set this matrix to be a simple scale matrix which scales the base axes by xyz.x, xyz.y and xyz.z respectively. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix use {@link #scale(Vector3dc) scale()} instead. + * + * @see #scale(Vector3dc) + * + * @param xyz + * the scale in x, y and z respectively + * @return this + */ + public Matrix3d scaling(Vector3dc xyz) { + m00 = xyz.x(); + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = xyz.y(); + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = xyz.z(); + return this; + } + + public Matrix3d scale(Vector3dc xyz, Matrix3d dest) { + return scale(xyz.x(), xyz.y(), xyz.z(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @return this + */ + public Matrix3d scale(Vector3dc xyz) { + return scale(xyz.x(), xyz.y(), xyz.z(), this); + } + + public Matrix3d scale(double x, double y, double z, Matrix3d dest) { + // scale matrix elements: + // m00 = x, m11 = y, m22 = z + // all others = 0 + dest.m00 = m00 * x; + dest.m01 = m01 * x; + dest.m02 = m02 * x; + dest.m10 = m10 * y; + dest.m11 = m11 * y; + dest.m12 = m12 * y; + dest.m20 = m20 * z; + dest.m21 = m21 * z; + dest.m22 = m22 * z; + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix3d scale(double x, double y, double z) { + return scale(x, y, z, this); + } + + public Matrix3d scale(double xyz, Matrix3d dest) { + return scale(xyz, xyz, xyz, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(double, double, double) + * + * @param xyz + * the factor for all components + * @return this + */ + public Matrix3d scale(double xyz) { + return scale(xyz, xyz, xyz); + } + + public Matrix3d scaleLocal(double x, double y, double z, Matrix3d dest) { + double nm00 = x * m00; + double nm01 = y * m01; + double nm02 = z * m02; + double nm10 = x * m10; + double nm11 = y * m11; + double nm12 = z * m12; + double nm20 = x * m20; + double nm21 = y * m21; + double nm22 = z * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix3d scaleLocal(double x, double y, double z) { + return scaleLocal(x, y, z, this); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to post-multiply a rotation transformation directly to a + * matrix, use {@link #rotate(double, Vector3dc) rotate()} instead. + * + * @see #rotate(double, Vector3dc) + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about (needs to be {@link Vector3d#normalize() normalized}) + * @return this + */ + public Matrix3d rotation(double angle, Vector3dc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to post-multiply a rotation transformation directly to a + * matrix, use {@link #rotate(double, Vector3fc) rotate()} instead. + * + * @see #rotate(double, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix3d rotation(double angle, Vector3fc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4f}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4f) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix3d rotation(AxisAngle4f axisAngle) { + return rotation(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4d}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4d) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @return this + */ + public Matrix3d rotation(AxisAngle4d axisAngle) { + return rotation(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(double, double, double, double) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * + * @param angle + * the angle in radians + * @param x + * the x-component of the rotation axis + * @param y + * the y-component of the rotation axis + * @param z + * the z-component of the rotation axis + * @return this + */ + public Matrix3d rotation(double angle, double x, double y, double z) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + double C = 1.0 - cos; + double xy = x * y, xz = x * z, yz = y * z; + m00 = cos + x * x * C; + m10 = xy * C - z * sin; + m20 = xz * C + y * sin; + m01 = xy * C + z * sin; + m11 = cos + y * y * C; + m21 = yz * C - x * sin; + m02 = xz * C - y * sin; + m12 = yz * C + x * sin; + m22 = cos + z * z * C; + return this; + } + + /** + * Set this matrix to a rotation transformation about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3d rotationX(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = 1.0; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = cos; + m12 = sin; + m20 = 0.0; + m21 = -sin; + m22 = cos; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3d rotationY(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = cos; + m01 = 0.0; + m02 = -sin; + m10 = 0.0; + m11 = 1.0; + m12 = 0.0; + m20 = sin; + m21 = 0.0; + m22 = cos; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3d rotationZ(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = cos; + m01 = sin; + m02 = 0.0; + m10 = -sin; + m11 = cos; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + return this; + } + + /** + * Set this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix3d rotationXYZ(double angleX, double angleY, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + + // rotateX + double nm11 = cosX; + double nm12 = sinX; + double nm21 = m_sinX; + double nm22 = cosX; + // rotateY + double nm00 = cosY; + double nm01 = nm21 * m_sinY; + double nm02 = nm22 * m_sinY; + m20 = sinY; + m21 = nm21 * cosY; + m22 = nm22 * cosY; + // rotateZ + m00 = nm00 * cosZ; + m01 = nm01 * cosZ + nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ; + m11 = nm01 * m_sinZ + nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + return this; + } + + /** + * Set this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix3d rotationZYX(double angleZ, double angleY, double angleX) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + + // rotateZ + double nm00 = cosZ; + double nm01 = sinZ; + double nm10 = m_sinZ; + double nm11 = cosZ; + // rotateY + double nm20 = nm00 * sinY; + double nm21 = nm01 * sinY; + double nm22 = cosY; + m00 = nm00 * cosY; + m01 = nm01 * cosY; + m02 = m_sinY; + // rotateX + m10 = nm10 * cosX + nm20 * sinX; + m11 = nm11 * cosX + nm21 * sinX; + m12 = nm22 * sinX; + m20 = nm10 * m_sinX + nm20 * cosX; + m21 = nm11 * m_sinX + nm21 * cosX; + m22 = nm22 * cosX; + return this; + } + + /** + * Set this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix3d rotationYXZ(double angleY, double angleX, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm00 = cosY; + double nm02 = m_sinY; + double nm20 = sinY; + double nm22 = cosY; + // rotateX + double nm10 = nm20 * sinX; + double nm11 = cosX; + double nm12 = nm22 * sinX; + m20 = nm20 * cosX; + m21 = m_sinX; + m22 = nm22 * cosX; + // rotateZ + m00 = nm00 * cosZ + nm10 * sinZ; + m01 = nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ + nm10 * cosZ; + m11 = nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + return this; + } + + /** + * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaterniondc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaterniondc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix3d rotation(Quaterniondc quat) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw; + double xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz; + double yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz; + double xw = quat.x() * quat.w(), dxw = xw + xw; + m00 = w2 + x2 - z2 - y2; + m01 = dxy + dzw; + m02 = dxz - dyw; + m10 = -dzw + dxy; + m11 = y2 - z2 + w2 - x2; + m12 = dyz + dxw; + m20 = dyw + dxz; + m21 = dyz - dxw; + m22 = z2 - y2 - x2 + w2; + return this; + } + + /** + * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaternionfc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaternionfc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix3d rotation(Quaternionfc quat) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw; + double xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz; + double yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz; + double xw = quat.x() * quat.w(), dxw = xw + xw; + m00 = w2 + x2 - z2 - y2; + m01 = dxy + dzw; + m02 = dxz - dyw; + m10 = -dzw + dxy; + m11 = y2 - z2 + w2 - x2; + m12 = dyz + dxw; + m20 = dyw + dxz; + m21 = dyz - dxw; + m22 = z2 - y2 - x2 + w2; + return this; + } + + public Vector3d transform(Vector3d v) { + return v.mul(this); + } + + public Vector3d transform(Vector3dc v, Vector3d dest) { + v.mul(this, dest); + return dest; + } + + public Vector3f transform(Vector3f v) { + return v.mul(this); + } + + public Vector3f transform(Vector3fc v, Vector3f dest) { + return v.mul(this, dest); + } + + public Vector3d transform(double x, double y, double z, Vector3d dest) { + return dest.set(Math.fma(m00, x, Math.fma(m10, y, m20 * z)), + Math.fma(m01, x, Math.fma(m11, y, m21 * z)), + Math.fma(m02, x, Math.fma(m12, y, m22 * z))); + } + + public Vector3d transformTranspose(Vector3d v) { + return v.mulTranspose(this); + } + + public Vector3d transformTranspose(Vector3dc v, Vector3d dest) { + return v.mulTranspose(this, dest); + } + + public Vector3d transformTranspose(double x, double y, double z, Vector3d dest) { + return dest.set(Math.fma(m00, x, Math.fma(m01, y, m02 * z)), + Math.fma(m10, x, Math.fma(m11, y, m12 * z)), + Math.fma(m20, x, Math.fma(m21, y, m22 * z))); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(m00); + out.writeDouble(m01); + out.writeDouble(m02); + out.writeDouble(m10); + out.writeDouble(m11); + out.writeDouble(m12); + out.writeDouble(m20); + out.writeDouble(m21); + out.writeDouble(m22); + } + + public void readExternal(ObjectInput in) throws IOException { + m00 = in.readDouble(); + m01 = in.readDouble(); + m02 = in.readDouble(); + m10 = in.readDouble(); + m11 = in.readDouble(); + m12 = in.readDouble(); + m20 = in.readDouble(); + m21 = in.readDouble(); + m22 = in.readDouble(); + } + + public Matrix3d rotateX(double ang, Matrix3d dest) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + double rm11 = cos; + double rm21 = -sin; + double rm12 = sin; + double rm22 = cos; + + // add temporaries for dependent values + double nm10 = m10 * rm11 + m20 * rm12; + double nm11 = m11 * rm11 + m21 * rm12; + double nm12 = m12 * rm11 + m22 * rm12; + // set non-dependent values directly + dest.m20 = m10 * rm21 + m20 * rm22; + dest.m21 = m11 * rm21 + m21 * rm22; + dest.m22 = m12 * rm21 + m22 * rm22; + // set other values + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + return dest; + } + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3d rotateX(double ang) { + return rotateX(ang, this); + } + + public Matrix3d rotateY(double ang, Matrix3d dest) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + double rm00 = cos; + double rm20 = sin; + double rm02 = -sin; + double rm22 = cos; + + // add temporaries for dependent values + double nm00 = m00 * rm00 + m20 * rm02; + double nm01 = m01 * rm00 + m21 * rm02; + double nm02 = m02 * rm00 + m22 * rm02; + // set non-dependent values directly + dest.m20 = m00 * rm20 + m20 * rm22; + dest.m21 = m01 * rm20 + m21 * rm22; + dest.m22 = m02 * rm20 + m22 * rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + return dest; + } + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3d rotateY(double ang) { + return rotateY(ang, this); + } + + public Matrix3d rotateZ(double ang, Matrix3d dest) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + double rm00 = cos; + double rm10 = -sin; + double rm01 = sin; + double rm11 = cos; + + // add temporaries for dependent values + double nm00 = m00 * rm00 + m10 * rm01; + double nm01 = m01 * rm00 + m11 * rm01; + double nm02 = m02 * rm00 + m12 * rm01; + // set non-dependent values directly + dest.m10 = m00 * rm10 + m10 * rm11; + dest.m11 = m01 * rm10 + m11 * rm11; + dest.m12 = m02 * rm10 + m12 * rm11; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + return dest; + } + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3d rotateZ(double ang) { + return rotateZ(ang, this); + } + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix3d rotateXYZ(double angleX, double angleY, double angleZ) { + return rotateXYZ(angleX, angleY, angleZ, this); + } + + public Matrix3d rotateXYZ(double angleX, double angleY, double angleZ, Matrix3d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + + // rotateX + double nm10 = m10 * cosX + m20 * sinX; + double nm11 = m11 * cosX + m21 * sinX; + double nm12 = m12 * cosX + m22 * sinX; + double nm20 = m10 * m_sinX + m20 * cosX; + double nm21 = m11 * m_sinX + m21 * cosX; + double nm22 = m12 * m_sinX + m22 * cosX; + // rotateY + double nm00 = m00 * cosY + nm20 * m_sinY; + double nm01 = m01 * cosY + nm21 * m_sinY; + double nm02 = m02 * cosY + nm22 * m_sinY; + dest.m20 = m00 * sinY + nm20 * cosY; + dest.m21 = m01 * sinY + nm21 * cosY; + dest.m22 = m02 * sinY + nm22 * cosY; + // rotateZ + dest.m00 = nm00 * cosZ + nm10 * sinZ; + dest.m01 = nm01 * cosZ + nm11 * sinZ; + dest.m02 = nm02 * cosZ + nm12 * sinZ; + dest.m10 = nm00 * m_sinZ + nm10 * cosZ; + dest.m11 = nm01 * m_sinZ + nm11 * cosZ; + dest.m12 = nm02 * m_sinZ + nm12 * cosZ; + return dest; + } + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix3d rotateZYX(double angleZ, double angleY, double angleX) { + return rotateZYX(angleZ, angleY, angleX, this); + } + + public Matrix3d rotateZYX(double angleZ, double angleY, double angleX, Matrix3d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + + // rotateZ + double nm00 = m00 * cosZ + m10 * sinZ; + double nm01 = m01 * cosZ + m11 * sinZ; + double nm02 = m02 * cosZ + m12 * sinZ; + double nm10 = m00 * m_sinZ + m10 * cosZ; + double nm11 = m01 * m_sinZ + m11 * cosZ; + double nm12 = m02 * m_sinZ + m12 * cosZ; + // rotateY + double nm20 = nm00 * sinY + m20 * cosY; + double nm21 = nm01 * sinY + m21 * cosY; + double nm22 = nm02 * sinY + m22 * cosY; + dest.m00 = nm00 * cosY + m20 * m_sinY; + dest.m01 = nm01 * cosY + m21 * m_sinY; + dest.m02 = nm02 * cosY + m22 * m_sinY; + // rotateX + dest.m10 = nm10 * cosX + nm20 * sinX; + dest.m11 = nm11 * cosX + nm21 * sinX; + dest.m12 = nm12 * cosX + nm22 * sinX; + dest.m20 = nm10 * m_sinX + nm20 * cosX; + dest.m21 = nm11 * m_sinX + nm21 * cosX; + dest.m22 = nm12 * m_sinX + nm22 * cosX; + return dest; + } + + /** + * Apply rotation of angles.y radians about the Y axis, followed by a rotation of angles.x radians about the X axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix3d rotateYXZ(Vector3d angles) { + return rotateYXZ(angles.y, angles.x, angles.z); + } + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix3d rotateYXZ(double angleY, double angleX, double angleZ) { + return rotateYXZ(angleY, angleX, angleZ, this); + } + + public Matrix3d rotateYXZ(double angleY, double angleX, double angleZ, Matrix3d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm20 = m00 * sinY + m20 * cosY; + double nm21 = m01 * sinY + m21 * cosY; + double nm22 = m02 * sinY + m22 * cosY; + double nm00 = m00 * cosY + m20 * m_sinY; + double nm01 = m01 * cosY + m21 * m_sinY; + double nm02 = m02 * cosY + m22 * m_sinY; + // rotateX + double nm10 = m10 * cosX + nm20 * sinX; + double nm11 = m11 * cosX + nm21 * sinX; + double nm12 = m12 * cosX + nm22 * sinX; + dest.m20 = m10 * m_sinX + nm20 * cosX; + dest.m21 = m11 * m_sinX + nm21 * cosX; + dest.m22 = m12 * m_sinX + nm22 * cosX; + // rotateZ + dest.m00 = nm00 * cosZ + nm10 * sinZ; + dest.m01 = nm01 * cosZ + nm11 * sinZ; + dest.m02 = nm02 * cosZ + nm12 * sinZ; + dest.m10 = nm00 * m_sinZ + nm10 * cosZ; + dest.m11 = nm01 * m_sinZ + nm11 * cosZ; + dest.m12 = nm02 * m_sinZ + nm12 * cosZ; + return dest; + } + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the given axis specified as x, y and z components. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix3d rotate(double ang, double x, double y, double z) { + return rotate(ang, x, y, z, this); + } + + public Matrix3d rotate(double ang, double x, double y, double z, Matrix3d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + + // rotation matrix elements: + // m30, m31, m32, m03, m13, m23 = 0 + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double rm00 = xx * C + c; + double rm01 = xy * C + z * s; + double rm02 = xz * C - y * s; + double rm10 = xy * C - z * s; + double rm11 = yy * C + c; + double rm12 = yz * C + x * s; + double rm20 = xz * C + y * s; + double rm21 = yz * C - x * s; + double rm22 = zz * C + c; + + // add temporaries for dependent values + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + // set non-dependent values directly + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotateLocal(double ang, double x, double y, double z, Matrix3d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double lm00 = xx * C + c; + double lm01 = xy * C + z * s; + double lm02 = xz * C - y * s; + double lm10 = xy * C - z * s; + double lm11 = yy * C + c; + double lm12 = yz * C + x * s; + double lm20 = xz * C + y * s; + double lm21 = yz * C - x * s; + double lm22 = zz * C + c; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix3d rotateLocal(double ang, double x, double y, double z) { + return rotateLocal(ang, x, y, z, this); + } + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(double) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(double) + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotateLocalX(double ang, Matrix3d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm01 = cos * m01 - sin * m02; + double nm02 = sin * m01 + cos * m02; + double nm11 = cos * m11 - sin * m12; + double nm12 = sin * m11 + cos * m12; + double nm21 = cos * m21 - sin * m22; + double nm22 = sin * m21 + cos * m22; + dest.m00 = m00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = m10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = m20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(double) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(double) + * + * @param ang + * the angle in radians to rotate about the X axis + * @return this + */ + public Matrix3d rotateLocalX(double ang) { + return rotateLocalX(ang, this); + } + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotateLocalY(double ang, Matrix3d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm00 = cos * m00 + sin * m02; + double nm02 = -sin * m00 + cos * m02; + double nm10 = cos * m10 + sin * m12; + double nm12 = -sin * m10 + cos * m12; + double nm20 = cos * m20 + sin * m22; + double nm22 = -sin * m20 + cos * m22; + dest.m00 = nm00; + dest.m01 = m01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = m11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = m21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @return this + */ + public Matrix3d rotateLocalY(double ang) { + return rotateLocalY(ang, this); + } + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(double) rotationZ()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationZ(double) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotateLocalZ(double ang, Matrix3d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm00 = cos * m00 - sin * m01; + double nm01 = sin * m00 + cos * m01; + double nm10 = cos * m10 - sin * m11; + double nm11 = sin * m10 + cos * m11; + double nm20 = cos * m20 - sin * m21; + double nm21 = sin * m20 + cos * m21; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = m02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = m12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = m22; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @return this + */ + public Matrix3d rotateLocalZ(double ang) { + return rotateLocalZ(ang, this); + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotateLocal(Quaterniondc quat, Matrix3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double lm00 = w2 + x2 - z2 - y2; + double lm01 = dxy + dzw; + double lm02 = dxz - dyw; + double lm10 = dxy - dzw; + double lm11 = y2 - z2 + w2 - x2; + double lm12 = dyz + dxw; + double lm20 = dyw + dxz; + double lm21 = dyz - dxw; + double lm22 = z2 - y2 - x2 + w2; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix3d rotateLocal(Quaterniondc quat) { + return rotateLocal(quat, this); + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotateLocal(Quaternionfc quat, Matrix3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double lm00 = w2 + x2 - z2 - y2; + double lm01 = dxy + dzw; + double lm02 = dxz - dyw; + double lm10 = dxy - dzw; + double lm11 = y2 - z2 + w2 - x2; + double lm12 = dyz + dxw; + double lm20 = dyw + dxz; + double lm21 = dyz - dxw; + double lm22 = z2 - y2 - x2 + w2; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix3d rotateLocal(Quaternionfc quat) { + return rotateLocal(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix3d rotate(Quaterniondc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotate(Quaterniondc quat, Matrix3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = dxy - dzw; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix3d rotate(Quaternionfc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotate(Quaternionfc quat, Matrix3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = dxy - dzw; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f}, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix3d rotate(AxisAngle4f axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotate(AxisAngle4f axisAngle, Matrix3d dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d}, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4d)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @return this + */ + public Matrix3d rotate(AxisAngle4d axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4d)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotate(AxisAngle4d axisAngle, Matrix3d dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3dc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3dc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @return this + */ + public Matrix3d rotate(double angle, Vector3dc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis and angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3dc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3dc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotate(double angle, Vector3dc axis, Matrix3d dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix3d rotate(double angle, Vector3fc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis and angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotate(double angle, Vector3fc axis, Matrix3d dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + public Vector3d getRow(int row, Vector3d dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + return dest.set(m00, m10, m20); + case 1: + return dest.set(m01, m11, m21); + case 2: + return dest.set(m02, m12, m22); + default: + throw new IndexOutOfBoundsException(); + } + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param src + * the row components to set + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + public Matrix3d setRow(int row, Vector3dc src) throws IndexOutOfBoundsException { + return setRow(row, src.x(), src.y(), src.z()); + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the column index in [0..2] + * @param x + * the first element in the row + * @param y + * the second element in the row + * @param z + * the third element in the row + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + public Matrix3d setRow(int row, double x, double y, double z) throws IndexOutOfBoundsException { + switch (row) { + case 0: + this.m00 = x; + this.m10 = y; + this.m20 = z; + break; + case 1: + this.m01 = x; + this.m11 = y; + this.m21 = z; + break; + case 2: + this.m02 = x; + this.m12 = y; + this.m22 = z; + break; + default: + throw new IndexOutOfBoundsException(); + } + return this; + } + + public Vector3d getColumn(int column, Vector3d dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + return dest.set(m00, m01, m02); + case 1: + return dest.set(m10, m11, m12); + case 2: + return dest.set(m20, m21, m22); + default: + throw new IndexOutOfBoundsException(); + } + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..2] + * @param src + * the column components to set + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..2] + */ + public Matrix3d setColumn(int column, Vector3dc src) throws IndexOutOfBoundsException { + return setColumn(column, src.x(), src.y(), src.z()); + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..2] + * @param x + * the first element in the column + * @param y + * the second element in the column + * @param z + * the third element in the column + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..2] + */ + public Matrix3d setColumn(int column, double x, double y, double z) throws IndexOutOfBoundsException { + switch (column) { + case 0: + this.m00 = x; + this.m01 = y; + this.m02 = z; + break; + case 1: + this.m10 = x; + this.m11 = y; + this.m12 = z; + break; + case 2: + this.m20 = x; + this.m21 = y; + this.m22 = z; + break; + default: + throw new IndexOutOfBoundsException(); + } + return this; + } + + public double get(int column, int row) { + return MemUtil.INSTANCE.get(this, column, row); + } + + /** + * Set the matrix element at the given column and row to the specified value. + * + * @param column + * the colum index in [0..2] + * @param row + * the row index in [0..2] + * @param value + * the value + * @return this + */ + public Matrix3d set(int column, int row, double value) { + return MemUtil.INSTANCE.set(this, column, row, value); + } + + public double getRowColumn(int row, int column) { + return MemUtil.INSTANCE.get(this, column, row); + } + + /** + * Set the matrix element at the given row and column to the specified value. + * + * @param row + * the row index in [0..2] + * @param column + * the colum index in [0..2] + * @param value + * the value + * @return this + */ + public Matrix3d setRowColumn(int row, int column, double value) { + return MemUtil.INSTANCE.set(this, column, row, value); + } + + /** + * Set this matrix to its own normal matrix. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In this case, use {@link #set(Matrix3dc)} to set a given Matrix3f to this matrix. + * + * @see #set(Matrix3dc) + * + * @return this + */ + public Matrix3d normal() { + return normal(this); + } + + /** + * Compute a normal matrix from this matrix and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In this case, use {@link #set(Matrix3dc)} to set a given Matrix3d to this matrix. + * + * @see #set(Matrix3dc) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d normal(Matrix3d dest) { + double m00m11 = m00 * m11; + double m01m10 = m01 * m10; + double m02m10 = m02 * m10; + double m00m12 = m00 * m12; + double m01m12 = m01 * m12; + double m02m11 = m02 * m11; + double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + double s = 1.0 / det; + /* Invert and transpose in one go */ + double nm00 = (m11 * m22 - m21 * m12) * s; + double nm01 = (m20 * m12 - m10 * m22) * s; + double nm02 = (m10 * m21 - m20 * m11) * s; + double nm10 = (m21 * m02 - m01 * m22) * s; + double nm11 = (m00 * m22 - m20 * m02) * s; + double nm12 = (m20 * m01 - m00 * m21) * s; + double nm20 = (m01m12 - m02m11) * s; + double nm21 = (m02m10 - m00m12) * s; + double nm22 = (m00m11 - m01m10) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Compute the cofactor matrix of this. + *

+ * The cofactor matrix can be used instead of {@link #normal()} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @return this + */ + public Matrix3d cofactor() { + return cofactor(this); + } + + /** + * Compute the cofactor matrix of this and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d cofactor(Matrix3d dest) { + double nm00 = m11 * m22 - m21 * m12; + double nm01 = m20 * m12 - m10 * m22; + double nm02 = m10 * m21 - m20 * m11; + double nm10 = m21 * m02 - m01 * m22; + double nm11 = m00 * m22 - m20 * m02; + double nm12 = m20 * m01 - m00 * m21; + double nm20 = m01 * m12 - m11 * m02; + double nm21 = m02 * m10 - m12 * m00; + double nm22 = m00 * m11 - m10 * m01; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3dc, Vector3dc) setLookAlong()}. + * + * @see #lookAlong(double, double, double, double, double, double) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix3d lookAlong(Vector3dc dir, Vector3dc up) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3dc, Vector3dc) setLookAlong()}. + * + * @see #lookAlong(double, double, double, double, double, double) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d lookAlong(Vector3dc dir, Vector3dc up, Matrix3d dest) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()} + * + * @see #setLookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d lookAlong(double dirX, double dirY, double dirZ, + double upX, double upY, double upZ, Matrix3d dest) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + double rm00 = leftX; + double rm01 = upnX; + double rm02 = dirX; + double rm10 = leftY; + double rm11 = upnY; + double rm12 = dirY; + double rm20 = leftZ; + double rm21 = upnZ; + double rm22 = dirZ; + + // perform optimized matrix multiplication + // introduce temporaries for dependent results + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set the rest of the matrix elements + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()} + * + * @see #setLookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix3d lookAlong(double dirX, double dirY, double dirZ, + double upX, double upY, double upZ) { + return lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(Vector3dc, Vector3dc)}. + * + * @see #setLookAlong(Vector3dc, Vector3dc) + * @see #lookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix3d setLookAlong(Vector3dc dir, Vector3dc up) { + return setLookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(double, double, double, double, double, double) lookAlong()} + * + * @see #setLookAlong(double, double, double, double, double, double) + * @see #lookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix3d setLookAlong(double dirX, double dirY, double dirZ, + double upX, double upY, double upZ) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + m00 = leftX; + m01 = upnX; + m02 = dirX; + m10 = leftY; + m11 = upnY; + m12 = dirY; + m20 = leftZ; + m21 = upnZ; + m22 = dirZ; + + return this; + } + + public Vector3d getScale(Vector3d dest) { + dest.x = Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02); + dest.y = Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12); + dest.z = Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22); + return dest; + } + + public Vector3d positiveZ(Vector3d dir) { + dir.x = m10 * m21 - m11 * m20; + dir.y = m20 * m01 - m21 * m00; + dir.z = m00 * m11 - m01 * m10; + return dir.normalize(dir); + } + + public Vector3d normalizedPositiveZ(Vector3d dir) { + dir.x = m02; + dir.y = m12; + dir.z = m22; + return dir; + } + + public Vector3d positiveX(Vector3d dir) { + dir.x = m11 * m22 - m12 * m21; + dir.y = m02 * m21 - m01 * m22; + dir.z = m01 * m12 - m02 * m11; + return dir.normalize(dir); + } + + public Vector3d normalizedPositiveX(Vector3d dir) { + dir.x = m00; + dir.y = m10; + dir.z = m20; + return dir; + } + + public Vector3d positiveY(Vector3d dir) { + dir.x = m12 * m20 - m10 * m22; + dir.y = m00 * m22 - m02 * m20; + dir.z = m02 * m10 - m00 * m12; + return dir.normalize(dir); + } + + public Vector3d normalizedPositiveY(Vector3d dir) { + dir.x = m01; + dir.y = m11; + dir.z = m21; + return dir; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(m00); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m01); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m02); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m10); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m11); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m12); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m20); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m21); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m22); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Matrix3d other = (Matrix3d) obj; + if (Double.doubleToLongBits(m00) != Double.doubleToLongBits(other.m00)) + return false; + if (Double.doubleToLongBits(m01) != Double.doubleToLongBits(other.m01)) + return false; + if (Double.doubleToLongBits(m02) != Double.doubleToLongBits(other.m02)) + return false; + if (Double.doubleToLongBits(m10) != Double.doubleToLongBits(other.m10)) + return false; + if (Double.doubleToLongBits(m11) != Double.doubleToLongBits(other.m11)) + return false; + if (Double.doubleToLongBits(m12) != Double.doubleToLongBits(other.m12)) + return false; + if (Double.doubleToLongBits(m20) != Double.doubleToLongBits(other.m20)) + return false; + if (Double.doubleToLongBits(m21) != Double.doubleToLongBits(other.m21)) + return false; + if (Double.doubleToLongBits(m22) != Double.doubleToLongBits(other.m22)) + return false; + return true; + } + + public boolean equals(Matrix3dc m, double delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix3d)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m02, m.m02(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + if (!Runtime.equals(m12, m.m12(), delta)) + return false; + if (!Runtime.equals(m20, m.m20(), delta)) + return false; + if (!Runtime.equals(m21, m.m21(), delta)) + return false; + if (!Runtime.equals(m22, m.m22(), delta)) + return false; + return true; + } + + /** + * Exchange the values of this matrix with the given other matrix. + * + * @param other + * the other matrix to exchange the values with + * @return this + */ + public Matrix3d swap(Matrix3d other) { + double tmp; + tmp = m00; m00 = other.m00; other.m00 = tmp; + tmp = m01; m01 = other.m01; other.m01 = tmp; + tmp = m02; m02 = other.m02; other.m02 = tmp; + tmp = m10; m10 = other.m10; other.m10 = tmp; + tmp = m11; m11 = other.m11; other.m11 = tmp; + tmp = m12; m12 = other.m12; other.m12 = tmp; + tmp = m20; m20 = other.m20; other.m20 = tmp; + tmp = m21; m21 = other.m21; other.m21 = tmp; + tmp = m22; m22 = other.m22; other.m22 = tmp; + return this; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix3d add(Matrix3dc other) { + return add(other, this); + } + + public Matrix3d add(Matrix3dc other, Matrix3d dest) { + dest.m00 = m00 + other.m00(); + dest.m01 = m01 + other.m01(); + dest.m02 = m02 + other.m02(); + dest.m10 = m10 + other.m10(); + dest.m11 = m11 + other.m11(); + dest.m12 = m12 + other.m12(); + dest.m20 = m20 + other.m20(); + dest.m21 = m21 + other.m21(); + dest.m22 = m22 + other.m22(); + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix3d sub(Matrix3dc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix3d sub(Matrix3dc subtrahend, Matrix3d dest) { + dest.m00 = m00 - subtrahend.m00(); + dest.m01 = m01 - subtrahend.m01(); + dest.m02 = m02 - subtrahend.m02(); + dest.m10 = m10 - subtrahend.m10(); + dest.m11 = m11 - subtrahend.m11(); + dest.m12 = m12 - subtrahend.m12(); + dest.m20 = m20 - subtrahend.m20(); + dest.m21 = m21 - subtrahend.m21(); + dest.m22 = m22 - subtrahend.m22(); + return dest; + } + + /** + * Component-wise multiply this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix3d mulComponentWise(Matrix3dc other) { + return mulComponentWise(other, this); + } + + public Matrix3d mulComponentWise(Matrix3dc other, Matrix3d dest) { + dest.m00 = m00 * other.m00(); + dest.m01 = m01 * other.m01(); + dest.m02 = m02 * other.m02(); + dest.m10 = m10 * other.m10(); + dest.m11 = m11 * other.m11(); + dest.m12 = m12 * other.m12(); + dest.m20 = m20 * other.m20(); + dest.m21 = m21 * other.m21(); + dest.m22 = m22 * other.m22(); + return dest; + } + + /** + * Set this matrix to a skew-symmetric matrix using the following layout: + *

+     *  0,  a, -b
+     * -a,  0,  c
+     *  b, -c,  0
+     * 
+ * + * Reference: https://en.wikipedia.org + * + * @param a + * the value used for the matrix elements m01 and m10 + * @param b + * the value used for the matrix elements m02 and m20 + * @param c + * the value used for the matrix elements m12 and m21 + * @return this + */ + public Matrix3d setSkewSymmetric(double a, double b, double c) { + m00 = m11 = m22 = 0; + m01 = -a; + m02 = b; + m10 = a; + m12 = -c; + m20 = -b; + m21 = c; + return this; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Matrix3d lerp(Matrix3dc other, double t) { + return lerp(other, t, this); + } + + public Matrix3d lerp(Matrix3dc other, double t, Matrix3d dest) { + dest.m00 = Math.fma(other.m00() - m00, t, m00); + dest.m01 = Math.fma(other.m01() - m01, t, m01); + dest.m02 = Math.fma(other.m02() - m02, t, m02); + dest.m10 = Math.fma(other.m10() - m10, t, m10); + dest.m11 = Math.fma(other.m11() - m11, t, m11); + dest.m12 = Math.fma(other.m12() - m12, t, m12); + dest.m20 = Math.fma(other.m20() - m20, t, m20); + dest.m21 = Math.fma(other.m21() - m21, t, m21); + dest.m22 = Math.fma(other.m22() - m22, t, m22); + return dest; + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3dc, Vector3dc) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix3d().lookAlong(new Vector3d(dir).negate(), up).invert(), dest) + * + * @see #rotateTowards(double, double, double, double, double, double, Matrix3d) + * @see #rotationTowards(Vector3dc, Vector3dc) + * + * @param direction + * the direction to rotate towards + * @param up + * the model's up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotateTowards(Vector3dc direction, Vector3dc up, Matrix3d dest) { + return rotateTowards(direction.x(), direction.y(), direction.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3dc, Vector3dc) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix3d().lookAlong(new Vector3d(dir).negate(), up).invert()) + * + * @see #rotateTowards(double, double, double, double, double, double) + * @see #rotationTowards(Vector3dc, Vector3dc) + * + * @param direction + * the direction to orient towards + * @param up + * the up vector + * @return this + */ + public Matrix3d rotateTowards(Vector3dc direction, Vector3dc up) { + return rotateTowards(direction.x(), direction.y(), direction.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix3d().lookAlong(-dirX, -dirY, -dirZ, upX, upY, upZ).invert()) + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix3d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + return rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix3d().lookAlong(-dirX, -dirY, -dirZ, upX, upY, upZ).invert(), dest) + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix3d dest) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double ndirX = dirX * invDirLength; + double ndirY = dirY * invDirLength; + double ndirZ = dirZ * invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = ndirY * leftZ - ndirZ * leftY; + double upnY = ndirZ * leftX - ndirX * leftZ; + double upnZ = ndirX * leftY - ndirY * leftX; + double rm00 = leftX; + double rm01 = leftY; + double rm02 = leftZ; + double rm10 = upnX; + double rm11 = upnY; + double rm12 = upnZ; + double rm20 = ndirX; + double rm21 = ndirY; + double rm22 = ndirZ; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with center - eye. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAlong(new Vector3d(dir).negate(), up).invert() + * + * @see #rotationTowards(Vector3dc, Vector3dc) + * @see #rotateTowards(double, double, double, double, double, double) + * + * @param dir + * the direction to orient the local -z axis towards + * @param up + * the up vector + * @return this + */ + public Matrix3d rotationTowards(Vector3dc dir, Vector3dc up) { + return rotationTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with center - eye. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAlong(-dirX, -dirY, -dirZ, upX, upY, upZ).invert() + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix3d rotationTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double ndirX = dirX * invDirLength; + double ndirY = dirY * invDirLength; + double ndirZ = dirZ * invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = ndirY * leftZ - ndirZ * leftY; + double upnY = ndirZ * leftX - ndirX * leftZ; + double upnZ = ndirX * leftY - ndirY * leftX; + this.m00 = leftX; + this.m01 = leftY; + this.m02 = leftZ; + this.m10 = upnX; + this.m11 = upnY; + this.m12 = upnZ; + this.m20 = ndirX; + this.m21 = ndirY; + this.m22 = ndirZ; + return this; + } + + public Vector3d getEulerAnglesZYX(Vector3d dest) { + dest.x = Math.atan2(m12, m22); + dest.y = Math.atan2(-m02, Math.sqrt(1.0 - m02 * m02)); + dest.z = Math.atan2(m01, m00); + return dest; + } + + public Vector3d getEulerAnglesXYZ(Vector3d dest) { + dest.x = Math.atan2(-m21, m22); + dest.y = Math.atan2(m20, Math.sqrt(1.0 - m20 * m20)); + dest.z = Math.atan2(-m10, m00); + return dest; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a
+     * 0 1 b
+     * 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @return this + */ + public Matrix3d obliqueZ(double a, double b) { + this.m20 = m00 * a + m10 * b + m20; + this.m21 = m01 * a + m11 * b + m21; + this.m22 = m02 * a + m12 * b + m22; + return this; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a
+     * 0 1 b
+     * 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d obliqueZ(double a, double b, Matrix3d dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m00 * a + m10 * b + m20; + dest.m21 = m01 * a + m11 * b + m21; + dest.m22 = m02 * a + m12 * b + m22; + return dest; + } + + public Matrix3d reflect(double nx, double ny, double nz, Matrix3d dest) { + double da = nx + nx, db = ny + ny, dc = nz + nz; + double rm00 = 1.0 - da * nx; + double rm01 = -da * ny; + double rm02 = -da * nz; + double rm10 = -db * nx; + double rm11 = 1.0 - db * ny; + double rm12 = -db * nz; + double rm20 = -dc * nx; + double rm21 = -dc * ny; + double rm22 = 1.0 - dc * nz; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + return dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through the given plane + * specified via the plane normal. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @return this + */ + public Matrix3d reflect(double nx, double ny, double nz) { + return reflect(nx, ny, nz, this); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through the given plane + * specified via the plane normal. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @return this + */ + public Matrix3d reflect(Vector3dc normal) { + return reflect(normal.x(), normal.y(), normal.z()); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation + * @return this + */ + public Matrix3d reflect(Quaterniondc orientation) { + return reflect(orientation, this); + } + + public Matrix3d reflect(Quaterniondc orientation, Matrix3d dest) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + double normalX = (double) (orientation.x() * num3 + orientation.w() * num2); + double normalY = (double) (orientation.y() * num3 - orientation.w() * num1); + double normalZ = (double) (1.0 - (orientation.x() * num1 + orientation.y() * num2)); + return reflect(normalX, normalY, normalZ, dest); + } + + public Matrix3d reflect(Vector3dc normal, Matrix3d dest) { + return reflect(normal.x(), normal.y(), normal.z(), dest); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects through the given plane + * specified via the plane normal. + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @return this + */ + public Matrix3d reflection(double nx, double ny, double nz) { + double da = nx + nx, db = ny + ny, dc = nz + nz; + this._m00(1.0 - da * nx); + this._m01(-da * ny); + this._m02(-da * nz); + this._m10(-db * nx); + this._m11(1.0 - db * ny); + this._m12(-db * nz); + this._m20(-dc * nx); + this._m21(-dc * ny); + this._m22(1.0 - dc * nz); + return this; + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects through the given plane + * specified via the plane normal. + * + * @param normal + * the plane normal + * @return this + */ + public Matrix3d reflection(Vector3dc normal) { + return reflection(normal.x(), normal.y(), normal.z()); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects through a plane + * specified via the plane orientation. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + * + * @param orientation + * the plane orientation + * @return this + */ + public Matrix3d reflection(Quaterniondc orientation) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + double normalX = orientation.x() * num3 + orientation.w() * num2; + double normalY = orientation.y() * num3 - orientation.w() * num1; + double normalZ = 1.0 - (orientation.x() * num1 + orientation.y() * num2); + return reflection(normalX, normalY, normalZ); + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && + Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && + Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22); + } + + public double quadraticFormProduct(double x, double y, double z) { + double Axx = m00 * x + m10 * y + m20 * z; + double Axy = m01 * x + m11 * y + m21 * z; + double Axz = m02 * x + m12 * y + m22 * z; + return x * Axx + y * Axy + z * Axz; + } + + public double quadraticFormProduct(Vector3dc v) { + return quadraticFormProduct(v.x(), v.y(), v.z()); + } + + public double quadraticFormProduct(Vector3fc v) { + return quadraticFormProduct(v.x(), v.y(), v.z()); + } + + /** + * Multiply this by the matrix + *

+     * 1 0 0
+     * 0 0 1
+     * 0 1 0
+     * 
+ * + * @return this + */ + public Matrix3d mapXZY() { + return mapXZY(this); + } + public Matrix3d mapXZY(Matrix3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m20)._m11(m21)._m12(m22)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * 1 0  0
+     * 0 0 -1
+     * 0 1  0
+     * 
+ * + * @return this + */ + public Matrix3d mapXZnY() { + return mapXZnY(this); + } + public Matrix3d mapXZnY(Matrix3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m20)._m11(m21)._m12(m22)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0
+     * 0 -1  0
+     * 0  0 -1
+     * 
+ * + * @return this + */ + public Matrix3d mapXnYnZ() { + return mapXnYnZ(this); + } + public Matrix3d mapXnYnZ(Matrix3d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * 1  0 0
+     * 0  0 1
+     * 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix3d mapXnZY() { + return mapXnZY(this); + } + public Matrix3d mapXnZY(Matrix3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0
+     * 0  0 -1
+     * 0 -1  0
+     * 
+ * + * @return this + */ + public Matrix3d mapXnZnY() { + return mapXnZnY(this); + } + public Matrix3d mapXnZnY(Matrix3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0
+     * 1 0 0
+     * 0 0 1
+     * 
+ * + * @return this + */ + public Matrix3d mapYXZ() { + return mapYXZ(this); + } + public Matrix3d mapYXZ(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m00)._m11(m01)._m12(m02)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0
+     * 1 0  0
+     * 0 0 -1
+     * 
+ * + * @return this + */ + public Matrix3d mapYXnZ() { + return mapYXnZ(this); + } + public Matrix3d mapYXnZ(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m00)._m11(m01)._m12(m02)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1
+     * 1 0 0
+     * 0 1 0
+     * 
+ * + * @return this + */ + public Matrix3d mapYZX() { + return mapYZX(this); + } + public Matrix3d mapYZX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m20)._m11(m21)._m12(m22)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1
+     * 1 0  0
+     * 0 1  0
+     * 
+ * + * @return this + */ + public Matrix3d mapYZnX() { + return mapYZnX(this); + } + public Matrix3d mapYZnX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m20)._m11(m21)._m12(m22)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0
+     * 1  0 0
+     * 0  0 1
+     * 
+ * + * @return this + */ + public Matrix3d mapYnXZ() { + return mapYnXZ(this); + } + public Matrix3d mapYnXZ(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0
+     * 1  0  0
+     * 0  0 -1
+     * 
+ * + * @return this + */ + public Matrix3d mapYnXnZ() { + return mapYnXnZ(this); + } + public Matrix3d mapYnXnZ(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1
+     * 1  0 0
+     * 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix3d mapYnZX() { + return mapYnZX(this); + } + public Matrix3d mapYnZX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1
+     * 1  0  0
+     * 0 -1  0
+     * 
+ * + * @return this + */ + public Matrix3d mapYnZnX() { + return mapYnZnX(this); + } + public Matrix3d mapYnZnX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0
+     * 0 0 1
+     * 1 0 0
+     * 
+ * + * @return this + */ + public Matrix3d mapZXY() { + return mapZXY(this); + } + public Matrix3d mapZXY(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m00)._m11(m01)._m12(m02)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0
+     * 0 0 -1
+     * 1 0  0
+     * 
+ * + * @return this + */ + public Matrix3d mapZXnY() { + return mapZXnY(this); + } + public Matrix3d mapZXnY(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m00)._m11(m01)._m12(m02)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1
+     * 0 1 0
+     * 1 0 0
+     * 
+ * + * @return this + */ + public Matrix3d mapZYX() { + return mapZYX(this); + } + public Matrix3d mapZYX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m10)._m11(m11)._m12(m12)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1
+     * 0 1  0
+     * 1 0  0
+     * 
+ * + * @return this + */ + public Matrix3d mapZYnX() { + return mapZYnX(this); + } + public Matrix3d mapZYnX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m10)._m11(m11)._m12(m12)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0
+     * 0  0 1
+     * 1  0 0
+     * 
+ * + * @return this + */ + public Matrix3d mapZnXY() { + return mapZnXY(this); + } + public Matrix3d mapZnXY(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0
+     * 0  0 -1
+     * 1  0  0
+     * 
+ * + * @return this + */ + public Matrix3d mapZnXnY() { + return mapZnXnY(this); + } + public Matrix3d mapZnXnY(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1
+     * 0 -1 0
+     * 1  0 0
+     * 
+ * + * @return this + */ + public Matrix3d mapZnYX() { + return mapZnYX(this); + } + public Matrix3d mapZnYX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1
+     * 0 -1  0
+     * 1  0  0
+     * 
+ * + * @return this + */ + public Matrix3d mapZnYnX() { + return mapZnYnX(this); + } + public Matrix3d mapZnYnX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0
+     *  0 1  0
+     *  0 0 -1
+     * 
+ * + * @return this + */ + public Matrix3d mapnXYnZ() { + return mapnXYnZ(this); + } + public Matrix3d mapnXYnZ(Matrix3d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m10)._m11(m11)._m12(m12)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * -1 0 0
+     *  0 0 1
+     *  0 1 0
+     * 
+ * + * @return this + */ + public Matrix3d mapnXZY() { + return mapnXZY(this); + } + public Matrix3d mapnXZY(Matrix3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m20)._m11(m21)._m12(m22)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0
+     *  0 0 -1
+     *  0 1  0
+     * 
+ * + * @return this + */ + public Matrix3d mapnXZnY() { + return mapnXZnY(this); + } + public Matrix3d mapnXZnY(Matrix3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m20)._m11(m21)._m12(m22)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0
+     *  0 -1 0
+     *  0  0 1
+     * 
+ * + * @return this + */ + public Matrix3d mapnXnYZ() { + return mapnXnYZ(this); + } + public Matrix3d mapnXnYZ(Matrix3d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0
+     *  0 -1  0
+     *  0  0 -1
+     * 
+ * + * @return this + */ + public Matrix3d mapnXnYnZ() { + return mapnXnYnZ(this); + } + public Matrix3d mapnXnYnZ(Matrix3d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0
+     *  0  0 1
+     *  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix3d mapnXnZY() { + return mapnXnZY(this); + } + public Matrix3d mapnXnZY(Matrix3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0
+     *  0  0 -1
+     *  0 -1  0
+     * 
+ * + * @return this + */ + public Matrix3d mapnXnZnY() { + return mapnXnZnY(this); + } + public Matrix3d mapnXnZnY(Matrix3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0
+     * -1 0 0
+     *  0 0 1
+     * 
+ * + * @return this + */ + public Matrix3d mapnYXZ() { + return mapnYXZ(this); + } + public Matrix3d mapnYXZ(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m00)._m11(m01)._m12(m02)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0
+     * -1 0  0
+     *  0 0 -1
+     * 
+ * + * @return this + */ + public Matrix3d mapnYXnZ() { + return mapnYXnZ(this); + } + public Matrix3d mapnYXnZ(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m00)._m11(m01)._m12(m02)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1
+     * -1 0 0
+     *  0 1 0
+     * 
+ * + * @return this + */ + public Matrix3d mapnYZX() { + return mapnYZX(this); + } + public Matrix3d mapnYZX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m20)._m11(m21)._m12(m22)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1
+     * -1 0  0
+     *  0 1  0
+     * 
+ * + * @return this + */ + public Matrix3d mapnYZnX() { + return mapnYZnX(this); + } + public Matrix3d mapnYZnX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m20)._m11(m21)._m12(m22)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0
+     * -1  0 0
+     *  0  0 1
+     * 
+ * + * @return this + */ + public Matrix3d mapnYnXZ() { + return mapnYnXZ(this); + } + public Matrix3d mapnYnXZ(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0
+     * -1  0  0
+     *  0  0 -1
+     * 
+ * + * @return this + */ + public Matrix3d mapnYnXnZ() { + return mapnYnXnZ(this); + } + public Matrix3d mapnYnXnZ(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1
+     * -1  0 0
+     *  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix3d mapnYnZX() { + return mapnYnZX(this); + } + public Matrix3d mapnYnZX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1
+     * -1  0  0
+     *  0 -1  0
+     * 
+ * + * @return this + */ + public Matrix3d mapnYnZnX() { + return mapnYnZnX(this); + } + public Matrix3d mapnYnZnX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0
+     *  0 0 1
+     * -1 0 0
+     * 
+ * + * @return this + */ + public Matrix3d mapnZXY() { + return mapnZXY(this); + } + public Matrix3d mapnZXY(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m00)._m11(m01)._m12(m02)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0
+     *  0 0 -1
+     * -1 0  0
+     * 
+ * + * @return this + */ + public Matrix3d mapnZXnY() { + return mapnZXnY(this); + } + public Matrix3d mapnZXnY(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m00)._m11(m01)._m12(m02)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1
+     *  0 1 0
+     * -1 0 0
+     * 
+ * + * @return this + */ + public Matrix3d mapnZYX() { + return mapnZYX(this); + } + public Matrix3d mapnZYX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m10)._m11(m11)._m12(m12)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1
+     *  0 1  0
+     * -1 0  0
+     * 
+ * + * @return this + */ + public Matrix3d mapnZYnX() { + return mapnZYnX(this); + } + public Matrix3d mapnZYnX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m10)._m11(m11)._m12(m12)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0
+     *  0  0 1
+     * -1  0 0
+     * 
+ * + * @return this + */ + public Matrix3d mapnZnXY() { + return mapnZnXY(this); + } + public Matrix3d mapnZnXY(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0
+     *  0  0 -1
+     * -1  0  0
+     * 
+ * + * @return this + */ + public Matrix3d mapnZnXnY() { + return mapnZnXnY(this); + } + public Matrix3d mapnZnXnY(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1
+     *  0 -1 0
+     * -1  0 0
+     * 
+ * + * @return this + */ + public Matrix3d mapnZnYX() { + return mapnZnYX(this); + } + public Matrix3d mapnZnYX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1
+     *  0 -1  0
+     * -1  0  0
+     * 
+ * + * @return this + */ + public Matrix3d mapnZnYnX() { + return mapnZnYnX(this); + } + public Matrix3d mapnZnYnX(Matrix3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m00)._m21(-m01)._m22(-m02); + } + + /** + * Multiply this by the matrix + *
+     * -1 0 0
+     *  0 1 0
+     *  0 0 1
+     * 
+ * + * @return this + */ + public Matrix3d negateX() { + return _m00(-m00)._m01(-m01)._m02(-m02); + } + public Matrix3d negateX(Matrix3d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m10)._m11(m11)._m12(m12)._m20(m20)._m21(m21)._m22(m22); + } + + /** + * Multiply this by the matrix + *
+     * 1  0 0
+     * 0 -1 0
+     * 0  0 1
+     * 
+ * + * @return this + */ + public Matrix3d negateY() { + return _m10(-m10)._m11(-m11)._m12(-m12); + } + public Matrix3d negateY(Matrix3d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m20)._m21(m21)._m22(m22); + } + + /** + * Multiply this by the matrix + *
+     * 1 0  0
+     * 0 1  0
+     * 0 0 -1
+     * 
+ * + * @return this + */ + public Matrix3d negateZ() { + return _m20(-m20)._m21(-m21)._m22(-m22); + } + public Matrix3d negateZ(Matrix3d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m10)._m11(m11)._m12(m12)._m20(-m20)._m21(-m21)._m22(-m22); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3dStack.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3dStack.java new file mode 100644 index 000000000..a522ec31b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3dStack.java @@ -0,0 +1,186 @@ +/* + * The MIT License + * + * Copyright (c) 2018-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A stack of many {@link Matrix3d} instances. This resembles the matrix stack known from legacy OpenGL. + *

+ * This {@link Matrix3dStack} class inherits from {@link Matrix3d}, so the current/top matrix is always the + * {@link Matrix3dStack}/{@link Matrix3d} itself. This affects all operations in {@link Matrix3d} that take another + * {@link Matrix3d} as parameter. If a {@link Matrix3dStack} is used as argument to those methods, the effective + * argument will always be the current matrix of the matrix stack. + * + * @author Kai Burjack + */ +public class Matrix3dStack extends Matrix3d { + + private static final long serialVersionUID = 1L; + + /** + * The matrix stack as a non-growable array. The size of the stack must be specified in the {@link #Matrix3dStack(int) constructor}. + */ + private Matrix3d[] mats; + + /** + * The index of the "current" matrix within {@link #mats}. + */ + private int curr; + + /** + * Create a new {@link Matrix3dStack} of the given size. + *

+ * Initially the stack pointer is at zero and the current matrix is set to identity. + * + * @param stackSize + * the size of the stack. This must be at least 1, in which case the {@link Matrix3dStack} simply only consists of this + * {@link Matrix3d} + */ + public Matrix3dStack(int stackSize) { + if (stackSize < 1) { + throw new IllegalArgumentException("stackSize must be >= 1"); //$NON-NLS-1$ + } + mats = new Matrix3d[stackSize - 1]; + // Allocate all matrices up front to keep the promise of being "allocation-free" + for (int i = 0; i < mats.length; i++) { + mats[i] = new Matrix3d(); + } + } + + /** + * Do not invoke manually! Only meant for serialization. + *

+ * Invoking this constructor from client code will result in an inconsistent state of the + * created {@link Matrix3dStack} instance. + */ + public Matrix3dStack() { + /* Empty! */ + } + + /** + * Set the stack pointer to zero and set the current/bottom matrix to {@link #identity() identity}. + * + * @return this + */ + public Matrix3dStack clear() { + curr = 0; + identity(); + return this; + } + + /** + * Increment the stack pointer by one and set the values of the new current matrix to the one directly below it. + * + * @return this + */ + public Matrix3dStack pushMatrix() { + if (curr == mats.length) { + throw new IllegalStateException("max stack size of " + (curr + 1) + " reached"); //$NON-NLS-1$ //$NON-NLS-2$ + } + mats[curr++].set(this); + return this; + } + + /** + * Decrement the stack pointer by one. + *

+ * This will effectively dispose of the current matrix. + * + * @return this + */ + public Matrix3dStack popMatrix() { + if (curr == 0) { + throw new IllegalStateException("already at the bottom of the stack"); //$NON-NLS-1$ + } + set(mats[--curr]); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + curr; + for (int i = 0; i < curr; i++) { + result = prime * result + mats[i].hashCode(); + } + return result; + } + + /* + * Contract between Matrix3d and Matrix3dStack: + * + * - Matrix3d.equals(Matrix3dStack) is true iff all the 9 matrix elements are equal + * - Matrix3dStack.equals(Matrix3d) is true iff all the 9 matrix elements are equal + * - Matrix3dStack.equals(Matrix3dStack) is true iff all 9 matrix elements are equal AND the matrix arrays as well as the stack pointer are equal + * - everything else is inequal + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (obj instanceof Matrix3dStack) { + Matrix3dStack other = (Matrix3dStack) obj; + if (curr != other.curr) + return false; + for (int i = 0; i < curr; i++) { + if (!mats[i].equals(other.mats[i])) + return false; + } + } + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(curr); + for (int i = 0; i < curr; i++) { + out.writeObject(mats[i]); + } + } + + public void readExternal(ObjectInput in) throws IOException { + super.readExternal(in); + curr = in.readInt(); + mats = new Matrix3dStack[curr]; + for (int i = 0; i < curr; i++) { + Matrix3d m = new Matrix3d(); + m.readExternal(in); + mats[i] = m; + } + } + + public Object clone() throws CloneNotSupportedException { + Matrix3dStack cloned = (Matrix3dStack) super.clone(); + Matrix3d[] clonedMats = new Matrix3d[mats.length]; + for (int i = 0; i < mats.length; i++) + clonedMats[i] = (Matrix3d) mats[i].clone(); + cloned.mats = clonedMats; + return cloned; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3dc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3dc.java new file mode 100644 index 000000000..18a330ecd --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3dc.java @@ -0,0 +1,2310 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 3x3 matrix of double-precision floats. + * + * @author Kai Burjack + */ +public interface Matrix3dc { + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + double m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + double m01(); + + /** + * Return the value of the matrix element at column 0 and row 2. + * + * @return the value of the matrix element + */ + double m02(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + double m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + double m11(); + + /** + * Return the value of the matrix element at column 1 and row 2. + * + * @return the value of the matrix element + */ + double m12(); + + /** + * Return the value of the matrix element at column 2 and row 0. + * + * @return the value of the matrix element + */ + double m20(); + + /** + * Return the value of the matrix element at column 2 and row 1. + * + * @return the value of the matrix element + */ + double m21(); + + /** + * Return the value of the matrix element at column 2 and row 2. + * + * @return the value of the matrix element + */ + double m22(); + + /** + * Multiply this matrix by the supplied matrix and store the result in dest. + * This matrix will be the left one. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mul(Matrix3dc right, Matrix3d dest); + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in dest. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix3d mulLocal(Matrix3dc left, Matrix3d dest); + + /** + * Multiply this matrix by the supplied matrix and store the result in dest. + * This matrix will be the left one. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mul(Matrix3fc right, Matrix3d dest); + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + double determinant(); + + /** + * Invert this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d invert(Matrix3d dest); + + /** + * Transpose this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d transpose(Matrix3d dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3d get(Matrix3d dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link AxisAngle4f}. + * + * @see AxisAngle4f#set(Matrix3dc) + * + * @param dest + * the destination {@link AxisAngle4f} + * @return the passed in destination + */ + AxisAngle4f getRotation(AxisAngle4f dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the three column vectors of this matrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaternionf#setFromUnnormalized(Matrix3dc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getUnnormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the three column vectors of this matrix are normalized. + * + * @see Quaternionf#setFromNormalized(Matrix3dc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getNormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the three column vectors of this matrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaterniond#setFromUnnormalized(Matrix3dc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getUnnormalizedRotation(Quaterniond dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the three column vectors of this matrix are normalized. + * + * @see Quaterniond#setFromNormalized(Matrix3dc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getNormalizedRotation(Quaterniond dest); + + /** + * Store this matrix into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position} using column-major order. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer} at which + * the matrix is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get(DoubleBuffer buffer); + + /** + * Store this matrix into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index using column-major order. + *

+ * This method will not increment the position of the given {@link DoubleBuffer}. + * + * @param index + * the absolute position into the {@link DoubleBuffer} + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer get(int index, DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getFloats(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getFloats(int, ByteBuffer) + * + * @param buffer + * will receive the elements of this matrix as float values in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getFloats(ByteBuffer buffer); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the elements of this matrix as float values in column-major order + * @return the passed in buffer + */ + ByteBuffer getFloats(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix3dc getToAddress(long address); + + /** + * Store this matrix into the supplied double array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] get(double[] arr, int offset); + + /** + * Store this matrix into the supplied double array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(double[], int)}. + * + * @see #get(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] get(double[] arr); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied float array at the given offset. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given float array. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get(float[] arr, int offset); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied float array. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given float array. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get(float[] arr); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix3d scale(Vector3dc xyz, Matrix3d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix3d scale(double x, double y, double z, Matrix3d dest); + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(double, double, double, Matrix3d) + * + * @param xyz + * the factor for all components + * @param dest + * will hold the result + * @return dest + */ + Matrix3d scale(double xyz, Matrix3d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix3d scaleLocal(double x, double y, double z, Matrix3d dest); + + /** + * Transform the given vector by this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector3d transform(Vector3d v); + + /** + * Transform the given vector by this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transform(Vector3dc v, Vector3d dest); + + /** + * Transform the given vector by this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector3f transform(Vector3f v); + + /** + * Transform the given vector by this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transform(Vector3fc v, Vector3f dest); + + /** + * Transform the vector (x, y, z) by this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transform(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector by the transpose of this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector3d transformTranspose(Vector3d v); + + /** + * Transform the given vector by the transpose of this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformTranspose(Vector3dc v, Vector3d dest); + + /** + * Transform the vector (x, y, z) by the transpose of this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformTranspose(double x, double y, double z, Vector3d dest); + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateX(double ang, Matrix3d dest); + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateY(double ang, Matrix3d dest); + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateZ(double ang, Matrix3d dest); + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX, dest).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateXYZ(double angleX, double angleY, double angleZ, Matrix3d dest); + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ, dest).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateZYX(double angleZ, double angleY, double angleX, Matrix3d dest); + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY, dest).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateYXZ(double angleY, double angleX, double angleZ, Matrix3d dest); + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the given axis specified as x, y and z components, and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotate(double ang, double x, double y, double z, Matrix3d dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateLocal(double ang, double x, double y, double z, Matrix3d dest); + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateLocalX(double ang, Matrix3d dest); + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateLocalY(double ang, Matrix3d dest); + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateLocalZ(double ang, Matrix3d dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateLocal(Quaterniondc quat, Matrix3d dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateLocal(Quaternionfc quat, Matrix3d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotate(Quaterniondc quat, Matrix3d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotate(Quaternionfc quat, Matrix3d dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix3d) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotate(AxisAngle4f axisAngle, Matrix3d dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix3d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotate(AxisAngle4d axisAngle, Matrix3d dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis and angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix3d) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotate(double angle, Vector3dc axis, Matrix3d dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis and angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix3d) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotate(double angle, Vector3fc axis, Matrix3d dest); + + /** + * Get the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param dest + * will hold the row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + Vector3d getRow(int row, Vector3d dest) throws IndexOutOfBoundsException; + + /** + * Get the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..2] + * @param dest + * will hold the column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..2] + */ + Vector3d getColumn(int column, Vector3d dest) throws IndexOutOfBoundsException; + + /** + * Get the matrix element value at the given column and row. + * + * @param column + * the colum index in [0..2] + * @param row + * the row index in [0..2] + * @return the element value + */ + double get(int column, int row); + + /** + * Get the matrix element value at the given row and column. + * + * @param row + * the row index in [0..2] + * @param column + * the colum index in [0..2] + * @return the element value + */ + double getRowColumn(int row, int column); + + /** + * Compute a normal matrix from this matrix and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d normal(Matrix3d dest); + + /** + * Compute the cofactor matrix of this and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d cofactor(Matrix3d dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + * + * @see #lookAlong(double, double, double, double, double, double, Matrix3d) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix3d lookAlong(Vector3dc dir, Vector3dc up, Matrix3d dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix3d lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix3d dest); + + /** + * Get the scaling factors of this matrix for the three base axes. + * + * @param dest + * will hold the scaling factors for x, y and z + * @return dest + */ + Vector3d getScale(Vector3d dest); + + /** + * Obtain the direction of +Z before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3d inv = new Matrix3d(this).invert();
+     * inv.transform(dir.set(0, 0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveZ(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3d positiveZ(Vector3d dir); + + /** + * Obtain the direction of +Z before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3d inv = new Matrix3d(this).transpose();
+     * inv.transform(dir.set(0, 0, 1));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3d normalizedPositiveZ(Vector3d dir); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3d inv = new Matrix3d(this).invert();
+     * inv.transform(dir.set(1, 0, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3d positiveX(Vector3d dir); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3d inv = new Matrix3d(this).transpose();
+     * inv.transform(dir.set(1, 0, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3d normalizedPositiveX(Vector3d dir); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3d inv = new Matrix3d(this).invert();
+     * inv.transform(dir.set(0, 1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3d positiveY(Vector3d dir); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3d inv = new Matrix3d(this).transpose();
+     * inv.transform(dir.set(0, 1, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3d normalizedPositiveY(Vector3d dir); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix3d add(Matrix3dc other, Matrix3d dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix3d sub(Matrix3dc subtrahend, Matrix3d dest); + + /** + * Component-wise multiply this by other and store the result in dest. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mulComponentWise(Matrix3dc other, Matrix3d dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Matrix3d lerp(Matrix3dc other, double t, Matrix3d dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mul(new Matrix3d().lookAlong(new Vector3d(dir).negate(), up).invert(), dest) + * + * @see #rotateTowards(double, double, double, double, double, double, Matrix3d) + * + * @param direction + * the direction to rotate towards + * @param up + * the model's up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateTowards(Vector3dc direction, Vector3dc up, Matrix3d dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mul(new Matrix3d().lookAlong(-dirX, -dirY, -dirZ, upX, upY, upZ).invert(), dest) + * + * @see #rotateTowards(Vector3dc, Vector3dc, Matrix3d) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix3d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix3d dest); + + /** + * Extract the Euler angles from the rotation represented by this matrix and store the extracted Euler angles in dest. + *

+ * This method assumes that this matrix only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3d#x} field, the angle around Y in the {@link Vector3d#y} + * field and the angle around Z in the {@link Vector3d#z} field of the supplied {@link Vector3d} instance. + *

+ * Note that the returned Euler angles must be applied in the order X * Y * Z to obtain the identical matrix. + * This means that calling {@link Matrix3dc#rotateXYZ(double, double, double, Matrix3d)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix3d m = ...; // <- matrix only representing rotation
+     * Matrix3d n = new Matrix3d();
+     * n.rotateXYZ(m.getEulerAnglesXYZ(new Vector3d()));
+     * 
+ *

+ * Reference: http://en.wikipedia.org/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3d getEulerAnglesXYZ(Vector3d dest); + + /** + * Extract the Euler angles from the rotation represented by this matrix and store the extracted Euler angles in dest. + *

+ * This method assumes that this matrix only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3d#x} field, the angle around Y in the {@link Vector3d#y} + * field and the angle around Z in the {@link Vector3d#z} field of the supplied {@link Vector3d} instance. + *

+ * Note that the returned Euler angles must be applied in the order Z * Y * X to obtain the identical matrix. + * This means that calling {@link Matrix3dc#rotateZYX(double, double, double, Matrix3d)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix3d m = ...; // <- matrix only representing rotation
+     * Matrix3d n = new Matrix3d();
+     * n.rotateZYX(m.getEulerAnglesZYX(new Vector3d()));
+     * 
+ *

+ * Reference: http://en.wikipedia.org/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3d getEulerAnglesZYX(Vector3d dest); + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a
+     * 0 1 b
+     * 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + Matrix3d obliqueZ(double a, double b, Matrix3d dest); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix3dc m, double delta); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through the given plane + * specified via the plane normal (nx, ny, nz), and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param dest + * will hold the result + * @return this + */ + Matrix3d reflect(double nx, double ny, double nz, Matrix3d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through a plane + * specified via the plane orientation, and store the result in dest. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation + * @param dest + * will hold the result + * @return this + */ + Matrix3d reflect(Quaterniondc orientation, Matrix3d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through the given plane + * specified via the plane normal, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param dest + * will hold the result + * @return this + */ + Matrix3d reflect(Vector3dc normal, Matrix3d dest); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + * Compute (x, y, z)^T * this * (x, y, z). + * + * @param x + * the x coordinate of the vector to multiply + * @param y + * the y coordinate of the vector to multiply + * @param z + * the z coordinate of the vector to multiply + * @return the result + */ + double quadraticFormProduct(double x, double y, double z); + + /** + * Compute v^T * this * v. + * + * @param v + * the vector to multiply + * @return the result + */ + double quadraticFormProduct(Vector3dc v); + + /** + * Compute v^T * this * v. + * + * @param v + * the vector to multiply + * @return the result + */ + double quadraticFormProduct(Vector3fc v); + + /** + * Multiply this by the matrix + *

+     * 1 0 0
+     * 0 0 1
+     * 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapXZY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 1 0  0
+     * 0 0 -1
+     * 0 1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapXZnY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0
+     * 0 -1  0
+     * 0  0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapXnYnZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 1  0 0
+     * 0  0 1
+     * 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapXnZY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0
+     * 0  0 -1
+     * 0 -1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapXnZnY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0
+     * 1 0 0
+     * 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapYXZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0
+     * 1 0  0
+     * 0 0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapYXnZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1
+     * 1 0 0
+     * 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapYZX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1
+     * 1 0  0
+     * 0 1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapYZnX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0
+     * 1  0 0
+     * 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapYnXZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0
+     * 1  0  0
+     * 0  0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapYnXnZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1
+     * 1  0 0
+     * 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapYnZX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1
+     * 1  0  0
+     * 0 -1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapYnZnX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0
+     * 0 0 1
+     * 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapZXY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0
+     * 0 0 -1
+     * 1 0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapZXnY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1
+     * 0 1 0
+     * 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapZYX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1
+     * 0 1  0
+     * 1 0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapZYnX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0
+     * 0  0 1
+     * 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapZnXY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0
+     * 0  0 -1
+     * 1  0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapZnXnY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1
+     * 0 -1 0
+     * 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapZnYX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1
+     * 0 -1  0
+     * 1  0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapZnYnX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0
+     *  0 1  0
+     *  0 0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnXYnZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * -1 0 0
+     *  0 0 1
+     *  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnXZY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0
+     *  0 0 -1
+     *  0 1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnXZnY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0
+     *  0 -1 0
+     *  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnXnYZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0
+     *  0 -1  0
+     *  0  0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnXnYnZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0
+     *  0  0 1
+     *  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnXnZY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0
+     *  0  0 -1
+     *  0 -1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnXnZnY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0
+     * -1 0 0
+     *  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnYXZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0
+     * -1 0  0
+     *  0 0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnYXnZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1
+     * -1 0 0
+     *  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnYZX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1
+     * -1 0  0
+     *  0 1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnYZnX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0
+     * -1  0 0
+     *  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnYnXZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0
+     * -1  0  0
+     *  0  0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnYnXnZ(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1
+     * -1  0 0
+     *  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnYnZX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1
+     * -1  0  0
+     *  0 -1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnYnZnX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0
+     *  0 0 1
+     * -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnZXY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0
+     *  0 0 -1
+     * -1 0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnZXnY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1
+     *  0 1 0
+     * -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnZYX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1
+     *  0 1  0
+     * -1 0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnZYnX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0
+     *  0  0 1
+     * -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnZnXY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0
+     *  0  0 -1
+     * -1  0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnZnXnY(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1
+     *  0 -1 0
+     * -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnZnYX(Matrix3d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1
+     *  0 -1  0
+     * -1  0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d mapnZnYnX(Matrix3d dest); + + /** + * Multiply this by the matrix + *
+     * -1 0 0
+     *  0 1 0
+     *  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d negateX(Matrix3d dest); + + /** + * Multiply this by the matrix + *
+     * 1  0 0
+     * 0 -1 0
+     * 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d negateY(Matrix3d dest); + + /** + * Multiply this by the matrix + *
+     * 1 0  0
+     * 0 1  0
+     * 0 0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d negateZ(Matrix3d dest); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3f.java new file mode 100644 index 000000000..751127974 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3f.java @@ -0,0 +1,4920 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + + +/** + * Contains the definition of a 3x3 matrix of floats, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10 m20
+ * m01 m11 m21
+ * m02 m12 m22
+ * + * @author Richard Greenlees + * @author Kai Burjack + */ +public class Matrix3f implements Externalizable, Cloneable, Matrix3fc { + + private static final long serialVersionUID = 1L; + + public float m00, m01, m02; + public float m10, m11, m12; + public float m20, m21, m22; + + /** + * Create a new {@link Matrix3f} and set it to {@link #identity() identity}. + */ + public Matrix3f() { + m00 = 1.0f; + m11 = 1.0f; + m22 = 1.0f; + } + + /** + * Create a new {@link Matrix3f} by setting its uppper left 2x2 submatrix to the values of the given {@link Matrix2fc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix2fc} + */ + public Matrix3f(Matrix2fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix3f} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix3fc} to copy the values from + */ + public Matrix3f(Matrix3fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix3f} and make it a copy of the upper left 3x3 of the given {@link Matrix4fc}. + * + * @param mat + * the {@link Matrix4fc} to copy the values from + */ + public Matrix3f(Matrix4fc mat) { + set(mat); + } + + /** + * Create a new 3x3 matrix using the supplied float values. The order of the parameter is column-major, + * so the first three parameters specify the three elements of the first column. + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m02 + * the value of m02 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + * @param m12 + * the value of m12 + * @param m20 + * the value of m20 + * @param m21 + * the value of m21 + * @param m22 + * the value of m22 + */ + public Matrix3f(float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + } + + /** + * Create a new {@link Matrix3f} by reading its 9 float components from the given {@link FloatBuffer} + * at the buffer's current position. + *

+ * That FloatBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link FloatBuffer} to read the matrix values from + */ + public Matrix3f(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Matrix3f} and initialize its three columns using the supplied vectors. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + */ + public Matrix3f(Vector3fc col0, Vector3fc col1, Vector3fc col2) { + set(col0, col1, col2); + } + + public float m00() { + return m00; + } + public float m01() { + return m01; + } + public float m02() { + return m02; + } + public float m10() { + return m10; + } + public float m11() { + return m11; + } + public float m12() { + return m12; + } + public float m20() { + return m20; + } + public float m21() { + return m21; + } + public float m22() { + return m22; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + public Matrix3f m00(float m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + public Matrix3f m01(float m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2. + * + * @param m02 + * the new value + * @return this + */ + public Matrix3f m02(float m02) { + this.m02 = m02; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + public Matrix3f m10(float m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + public Matrix3f m11(float m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2. + * + * @param m12 + * the new value + * @return this + */ + public Matrix3f m12(float m12) { + this.m12 = m12; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + public Matrix3f m20(float m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + public Matrix3f m21(float m21) { + this.m21 = m21; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2. + * + * @param m22 + * the new value + * @return this + */ + public Matrix3f m22(float m22) { + this.m22 = m22; + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + Matrix3f _m00(float m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + Matrix3f _m01(float m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2. + * + * @param m02 + * the new value + * @return this + */ + Matrix3f _m02(float m02) { + this.m02 = m02; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + Matrix3f _m10(float m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + Matrix3f _m11(float m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2. + * + * @param m12 + * the new value + * @return this + */ + Matrix3f _m12(float m12) { + this.m12 = m12; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + Matrix3f _m20(float m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + Matrix3f _m21(float m21) { + this.m21 = m21; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2. + * + * @param m22 + * the new value + * @return this + */ + Matrix3f _m22(float m22) { + this.m22 = m22; + return this; + } + + /** + * Set the elements of this matrix to the ones in m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix3f set(Matrix3fc m) { + return + _m00(m.m00()). + _m01(m.m01()). + _m02(m.m02()). + _m10(m.m10()). + _m11(m.m11()). + _m12(m.m12()). + _m20(m.m20()). + _m21(m.m21()). + _m22(m.m22()); + } + + /** + * Store the values of the transpose of the given matrix m into this matrix. + * + * @param m + * the matrix to copy the transposed values from + * @return this + */ + public Matrix3f setTransposed(Matrix3fc m) { + float nm10 = m.m01(), nm12 = m.m21(); + float nm20 = m.m02(), nm21 = m.m12(); + return this + ._m00(m.m00())._m01(m.m10())._m02(m.m20()) + ._m10(nm10)._m11(m.m11())._m12(nm12) + ._m20(nm20)._m21(nm21)._m22(m.m22()); + } + + /** + * Set the elements of this matrix to the left 3x3 submatrix of m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix3f set(Matrix4x3fc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + return this; + } + + /** + * Set the elements of this matrix to the upper left 3x3 of the given {@link Matrix4fc}. + * + * @param mat + * the {@link Matrix4fc} to copy the values from + * @return this + */ + public Matrix3f set(Matrix4fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + return this; + } + + /** + * Set the upper left 2x2 submatrix of this {@link Matrix3f} to the given {@link Matrix2fc} + * and the rest to identity. + * + * @see #Matrix3f(Matrix2fc) + * + * @param mat + * the {@link Matrix2fc} + * @return this + */ + public Matrix3f set(Matrix2fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = 0.0f; + m10 = mat.m10(); + m11 = mat.m11(); + m12 = 0.0f; + m20 = 0.0f; + m21 = 0.0f; + m22 = 1.0f; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + * @return this + */ + public Matrix3f set(AxisAngle4f axisAngle) { + float x = axisAngle.x; + float y = axisAngle.y; + float z = axisAngle.z; + float angle = axisAngle.angle; + float invLength = Math.invsqrt(x*x + y*y + z*z); + x *= invLength; + y *= invLength; + z *= invLength; + float s = Math.sin(angle); + float c = Math.cosFromSin(s, angle); + float omc = 1.0f - c; + m00 = c + x*x*omc; + m11 = c + y*y*omc; + m22 = c + z*z*omc; + float tmp1 = x*y*omc; + float tmp2 = z*s; + m10 = tmp1 - tmp2; + m01 = tmp1 + tmp2; + tmp1 = x*z*omc; + tmp2 = y*s; + m20 = tmp1 + tmp2; + m02 = tmp1 - tmp2; + tmp1 = y*z*omc; + tmp2 = x*s; + m21 = tmp1 - tmp2; + m12 = tmp1 + tmp2; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + * @return this + */ + public Matrix3f set(AxisAngle4d axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double invLength = Math.invsqrt(x*x + y*y + z*z); + x *= invLength; + y *= invLength; + z *= invLength; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0f - c; + m00 = (float)(c + x*x*omc); + m11 = (float)(c + y*y*omc); + m22 = (float)(c + z*z*omc); + double tmp1 = x*y*omc; + double tmp2 = z*s; + m10 = (float)(tmp1 - tmp2); + m01 = (float)(tmp1 + tmp2); + tmp1 = x*z*omc; + tmp2 = y*s; + m20 = (float)(tmp1 + tmp2); + m02 = (float)(tmp1 - tmp2); + tmp1 = y*z*omc; + tmp2 = x*s; + m21 = (float)(tmp1 - tmp2); + m12 = (float)(tmp1 + tmp2); + return this; + } + + /** + * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaternionfc}. + *

+ * This method is equivalent to calling: rotation(q) + *

+ * Reference: http://www.euclideanspace.com/ + * + * @see #rotation(Quaternionfc) + * + * @param q + * the {@link Quaternionfc} + * @return this + */ + public Matrix3f set(Quaternionfc q) { + return rotation(q); + } + + /** + * Set this matrix to a rotation - and possibly scaling - equivalent to the given quaternion. + *

+ * Reference: http://www.euclideanspace.com/ + * + * @param q + * the quaternion + * @return this + */ + public Matrix3f set(Quaterniondc q) { + double w2 = q.w() * q.w(); + double x2 = q.x() * q.x(); + double y2 = q.y() * q.y(); + double z2 = q.z() * q.z(); + double zw = q.z() * q.w(); + double xy = q.x() * q.y(); + double xz = q.x() * q.z(); + double yw = q.y() * q.w(); + double yz = q.y() * q.z(); + double xw = q.x() * q.w(); + m00 = (float) (w2 + x2 - z2 - y2); + m01 = (float) (xy + zw + zw + xy); + m02 = (float) (xz - yw + xz - yw); + m10 = (float) (-zw + xy - zw + xy); + m11 = (float) (y2 - z2 + w2 - x2); + m12 = (float) (yz + yz + xw + xw); + m20 = (float) (yw + xz + xz + yw); + m21 = (float) (yz + yz - xw - xw); + m22 = (float) (z2 - y2 - x2 + w2); + return this; + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix3f mul(Matrix3fc right) { + return mul(right, this); + } + + public Matrix3f mul(Matrix3fc right, Matrix3f dest) { + float nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), m20 * right.m02())); + float nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), m21 * right.m02())); + float nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), m22 * right.m02())); + float nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), m20 * right.m12())); + float nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), m21 * right.m12())); + float nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), m22 * right.m12())); + float nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), m20 * right.m22())); + float nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), m21 * right.m22())); + float nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), m22 * right.m22())); + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in this. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @return this + */ + public Matrix3f mulLocal(Matrix3fc left) { + return mulLocal(left, this); + } + + public Matrix3f mulLocal(Matrix3fc left, Matrix3f dest) { + float nm00 = left.m00() * m00 + left.m10() * m01 + left.m20() * m02; + float nm01 = left.m01() * m00 + left.m11() * m01 + left.m21() * m02; + float nm02 = left.m02() * m00 + left.m12() * m01 + left.m22() * m02; + float nm10 = left.m00() * m10 + left.m10() * m11 + left.m20() * m12; + float nm11 = left.m01() * m10 + left.m11() * m11 + left.m21() * m12; + float nm12 = left.m02() * m10 + left.m12() * m11 + left.m22() * m12; + float nm20 = left.m00() * m20 + left.m10() * m21 + left.m20() * m22; + float nm21 = left.m01() * m20 + left.m11() * m21 + left.m21() * m22; + float nm22 = left.m02() * m20 + left.m12() * m21 + left.m22() * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Set the values within this matrix to the supplied float values. The result looks like this: + *

+ * m00, m10, m20
+ * m01, m11, m21
+ * m02, m12, m22
+ * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m02 + * the new value of m02 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @param m12 + * the new value of m12 + * @param m20 + * the new value of m20 + * @param m21 + * the new value of m21 + * @param m22 + * the new value of m22 + * @return this + */ + public Matrix3f set(float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + return this; + } + + /** + * Set the values in this matrix based on the supplied float array. The result looks like this: + *

+ * 0, 3, 6
+ * 1, 4, 7
+ * 2, 5, 8
+ * + * This method only uses the first 9 values, all others are ignored. + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix3f set(float m[]) { + MemUtil.INSTANCE.copy(m, 0, this); + return this; + } + + /** + * Set the three columns of this matrix to the supplied vectors, respectively. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @return this + */ + public Matrix3f set(Vector3fc col0, Vector3fc col1, Vector3fc col2) { + this.m00 = col0.x(); + this.m01 = col0.y(); + this.m02 = col0.z(); + this.m10 = col1.x(); + this.m11 = col1.y(); + this.m12 = col1.z(); + this.m20 = col2.x(); + this.m21 = col2.y(); + this.m22 = col2.z(); + return this; + } + + public float determinant() { + return (m00 * m11 - m01 * m10) * m22 + + (m02 * m10 - m00 * m12) * m21 + + (m01 * m12 - m02 * m11) * m20; + } + + /** + * Invert this matrix. + * + * @return this + */ + public Matrix3f invert() { + return invert(this); + } + + public Matrix3f invert(Matrix3f dest) { + float a = Math.fma(m00, m11, -m01 * m10); + float b = Math.fma(m02, m10, -m00 * m12); + float c = Math.fma(m01, m12, -m02 * m11); + float d = Math.fma(a, m22, Math.fma(b, m21, c * m20)); + float s = 1.0f / d; + float nm00 = Math.fma(m11, m22, -m21 * m12) * s; + float nm01 = Math.fma(m21, m02, -m01 * m22) * s; + float nm02 = c * s; + float nm10 = Math.fma(m20, m12, -m10 * m22) * s; + float nm11 = Math.fma(m00, m22, -m20 * m02) * s; + float nm12 = b * s; + float nm20 = Math.fma(m10, m21, -m20 * m11) * s; + float nm21 = Math.fma(m20, m01, -m00 * m21) * s; + float nm22 = a * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Transpose this matrix. + * + * @return this + */ + public Matrix3f transpose() { + return transpose(this); + } + + public Matrix3f transpose(Matrix3f dest) { + return dest.set(m00, m10, m20, + m01, m11, m21, + m02, m12, m22); + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + " " + Runtime.format(m20, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + " " + Runtime.format(m21, formatter) + "\n" + + Runtime.format(m02, formatter) + " " + Runtime.format(m12, formatter) + " " + Runtime.format(m22, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix3fc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix3fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix3f get(Matrix3f dest) { + return dest.set(this); + } + + public Matrix4f get(Matrix4f dest) { + return dest.set(this); + } + + public AxisAngle4f getRotation(AxisAngle4f dest) { + return dest.set(this); + } + + public Quaternionf getUnnormalizedRotation(Quaternionf dest) { + return dest.setFromUnnormalized(this); + } + + public Quaternionf getNormalizedRotation(Quaternionf dest) { + return dest.setFromNormalized(this); + } + + public Quaterniond getUnnormalizedRotation(Quaterniond dest) { + return dest.setFromUnnormalized(this); + } + + public Quaterniond getNormalizedRotation(Quaterniond dest) { + return dest.setFromNormalized(this); + } + + + public FloatBuffer get(FloatBuffer buffer) { + return get(buffer.position(), buffer); + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public FloatBuffer get3x4(FloatBuffer buffer) { + return get3x4(buffer.position(), buffer); + } + + public FloatBuffer get3x4(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put3x4(this, index, buffer); + return buffer; + } + + public ByteBuffer get3x4(ByteBuffer buffer) { + return get3x4(buffer.position(), buffer); + } + + public ByteBuffer get3x4(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put3x4(this, index, buffer); + return buffer; + } + + public FloatBuffer getTransposed(FloatBuffer buffer) { + return getTransposed(buffer.position(), buffer); + } + + public FloatBuffer getTransposed(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public ByteBuffer getTransposed(ByteBuffer buffer) { + return getTransposed(buffer.position(), buffer); + } + + public ByteBuffer getTransposed(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public Matrix3fc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + public float[] get(float[] arr, int offset) { + MemUtil.INSTANCE.copy(this, arr, offset); + return arr; + } + + public float[] get(float[] arr) { + return get(arr, 0); + } + + /** + * Set the values of this matrix by reading 9 float values from the given {@link FloatBuffer} in column-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3f set(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 float values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3f set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 float values from the given {@link FloatBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3f set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 9 float values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3f set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + /** + * Set the values of this matrix by reading 9 float values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix3f setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set all values within this matrix to zero. + * + * @return this + */ + public Matrix3f zero() { + MemUtil.INSTANCE.zero(this); + return this; + } + + /** + * Set this matrix to the identity. + * + * @return this + */ + public Matrix3f identity() { + MemUtil.INSTANCE.identity(this); + return this; + } + + public Matrix3f scale(Vector3fc xyz, Matrix3f dest) { + return scale(xyz.x(), xyz.y(), xyz.z(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @return this + */ + public Matrix3f scale(Vector3fc xyz) { + return scale(xyz.x(), xyz.y(), xyz.z(), this); + } + + public Matrix3f scale(float x, float y, float z, Matrix3f dest) { + // scale matrix elements: + // m00 = x, m11 = y, m22 = z + // all others = 0 + dest.m00 = m00 * x; + dest.m01 = m01 * x; + dest.m02 = m02 * x; + dest.m10 = m10 * y; + dest.m11 = m11 * y; + dest.m12 = m12 * y; + dest.m20 = m20 * z; + dest.m21 = m21 * z; + dest.m22 = m22 * z; + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix3f scale(float x, float y, float z) { + return scale(x, y, z, this); + } + + public Matrix3f scale(float xyz, Matrix3f dest) { + return scale(xyz, xyz, xyz, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(float, float, float) + * + * @param xyz + * the factor for all components + * @return this + */ + public Matrix3f scale(float xyz) { + return scale(xyz, xyz, xyz); + } + + public Matrix3f scaleLocal(float x, float y, float z, Matrix3f dest) { + float nm00 = x * m00; + float nm01 = y * m01; + float nm02 = z * m02; + float nm10 = x * m10; + float nm11 = y * m11; + float nm12 = z * m12; + float nm20 = x * m20; + float nm21 = y * m21; + float nm22 = z * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix3f scaleLocal(float x, float y, float z) { + return scaleLocal(x, y, z, this); + } + + /** + * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(float) scale()} instead. + * + * @see #scale(float) + * + * @param factor + * the scale factor in x, y and z + * @return this + */ + public Matrix3f scaling(float factor) { + MemUtil.INSTANCE.zero(this); + m00 = factor; + m11 = factor; + m22 = factor; + return this; + } + + /** + * Set this matrix to be a simple scale matrix. + * + * @param x + * the scale in x + * @param y + * the scale in y + * @param z + * the scale in z + * @return this + */ + public Matrix3f scaling(float x, float y, float z) { + MemUtil.INSTANCE.zero(this); + m00 = x; + m11 = y; + m22 = z; + return this; + } + + /** + * Set this matrix to be a simple scale matrix which scales the base axes by xyz.x, xyz.y and xyz.z respectively. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix use {@link #scale(Vector3fc) scale()} instead. + * + * @see #scale(Vector3fc) + * + * @param xyz + * the scale in x, y and z respectively + * @return this + */ + public Matrix3f scaling(Vector3fc xyz) { + return scaling(xyz.x(), xyz.y(), xyz.z()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to post-multiply a rotation transformation directly to a + * matrix, use {@link #rotate(float, Vector3fc) rotate()} instead. + * + * @see #rotate(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix3f rotation(float angle, Vector3fc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4f}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4f) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix3f rotation(AxisAngle4f axisAngle) { + return rotation(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(float, float, float, float) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * + * @param angle + * the angle in radians + * @param x + * the x-component of the rotation axis + * @param y + * the y-component of the rotation axis + * @param z + * the z-component of the rotation axis + * @return this + */ + public Matrix3f rotation(float angle, float x, float y, float z) { + float sin = Math.sin(angle); + float cos = Math.cosFromSin(sin, angle); + float C = 1.0f - cos; + float xy = x * y, xz = x * z, yz = y * z; + m00 = cos + x * x * C; + m10 = xy * C - z * sin; + m20 = xz * C + y * sin; + m01 = xy * C + z * sin; + m11 = cos + y * y * C; + m21 = yz * C - x * sin; + m02 = xz * C - y * sin; + m12 = yz * C + x * sin; + m22 = cos + z * z * C; + return this; + } + + /** + * Set this matrix to a rotation transformation about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3f rotationX(float ang) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = 1.0f; + m01 = 0.0f; + m02 = 0.0f; + m10 = 0.0f; + m11 = cos; + m12 = sin; + m20 = 0.0f; + m21 = -sin; + m22 = cos; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3f rotationY(float ang) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = cos; + m01 = 0.0f; + m02 = -sin; + m10 = 0.0f; + m11 = 1.0f; + m12 = 0.0f; + m20 = sin; + m21 = 0.0f; + m22 = cos; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3f rotationZ(float ang) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = cos; + m01 = sin; + m02 = 0.0f; + m10 = -sin; + m11 = cos; + m12 = 0.0f; + m20 = 0.0f; + m21 = 0.0f; + m22 = 1.0f; + return this; + } + + /** + * Set this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix3f rotationXYZ(float angleX, float angleY, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinX = -sinX; + float m_sinY = -sinY; + float m_sinZ = -sinZ; + + // rotateX + float nm11 = cosX; + float nm12 = sinX; + float nm21 = m_sinX; + float nm22 = cosX; + // rotateY + float nm00 = cosY; + float nm01 = nm21 * m_sinY; + float nm02 = nm22 * m_sinY; + m20 = sinY; + m21 = nm21 * cosY; + m22 = nm22 * cosY; + // rotateZ + m00 = nm00 * cosZ; + m01 = nm01 * cosZ + nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ; + m11 = nm01 * m_sinZ + nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + return this; + } + + /** + * Set this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix3f rotationZYX(float angleZ, float angleY, float angleX) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinZ = -sinZ; + float m_sinY = -sinY; + float m_sinX = -sinX; + + // rotateZ + float nm00 = cosZ; + float nm01 = sinZ; + float nm10 = m_sinZ; + float nm11 = cosZ; + // rotateY + float nm20 = nm00 * sinY; + float nm21 = nm01 * sinY; + float nm22 = cosY; + m00 = nm00 * cosY; + m01 = nm01 * cosY; + m02 = m_sinY; + // rotateX + m10 = nm10 * cosX + nm20 * sinX; + m11 = nm11 * cosX + nm21 * sinX; + m12 = nm22 * sinX; + m20 = nm10 * m_sinX + nm20 * cosX; + m21 = nm11 * m_sinX + nm21 * cosX; + m22 = nm22 * cosX; + return this; + } + + /** + * Set this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix3f rotationYXZ(float angleY, float angleX, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinY = -sinY; + float m_sinX = -sinX; + float m_sinZ = -sinZ; + + // rotateY + float nm00 = cosY; + float nm02 = m_sinY; + float nm20 = sinY; + float nm22 = cosY; + // rotateX + float nm10 = nm20 * sinX; + float nm11 = cosX; + float nm12 = nm22 * sinX; + m20 = nm20 * cosX; + m21 = m_sinX; + m22 = nm22 * cosX; + // rotateZ + m00 = nm00 * cosZ + nm10 * sinZ; + m01 = nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ + nm10 * cosZ; + m11 = nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + return this; + } + + /** + * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaternionfc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaternionfc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix3f rotation(Quaternionfc quat) { + float w2 = quat.w() * quat.w(); + float x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(); + float z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw; + float xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz; + float yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz; + float xw = quat.x() * quat.w(), dxw = xw + xw; + m00 = w2 + x2 - z2 - y2; + m01 = dxy + dzw; + m02 = dxz - dyw; + m10 = -dzw + dxy; + m11 = y2 - z2 + w2 - x2; + m12 = dyz + dxw; + m20 = dyw + dxz; + m21 = dyz - dxw; + m22 = z2 - y2 - x2 + w2; + return this; + } + + public Vector3f transform(Vector3f v) { + return v.mul(this); + } + + public Vector3f transform(Vector3fc v, Vector3f dest) { + return v.mul(this, dest); + } + + public Vector3f transform(float x, float y, float z, Vector3f dest) { + return dest.set(Math.fma(m00, x, Math.fma(m10, y, m20 * z)), + Math.fma(m01, x, Math.fma(m11, y, m21 * z)), + Math.fma(m02, x, Math.fma(m12, y, m22 * z))); + } + + public Vector3f transformTranspose(Vector3f v) { + return v.mulTranspose(this); + } + + public Vector3f transformTranspose(Vector3fc v, Vector3f dest) { + return v.mulTranspose(this, dest); + } + + public Vector3f transformTranspose(float x, float y, float z, Vector3f dest) { + return dest.set(Math.fma(m00, x, Math.fma(m01, y, m02 * z)), + Math.fma(m10, x, Math.fma(m11, y, m12 * z)), + Math.fma(m20, x, Math.fma(m21, y, m22 * z))); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(m00); + out.writeFloat(m01); + out.writeFloat(m02); + out.writeFloat(m10); + out.writeFloat(m11); + out.writeFloat(m12); + out.writeFloat(m20); + out.writeFloat(m21); + out.writeFloat(m22); + } + + public void readExternal(ObjectInput in) throws IOException { + m00 = in.readFloat(); + m01 = in.readFloat(); + m02 = in.readFloat(); + m10 = in.readFloat(); + m11 = in.readFloat(); + m12 = in.readFloat(); + m20 = in.readFloat(); + m21 = in.readFloat(); + m22 = in.readFloat(); + } + + public Matrix3f rotateX(float ang, Matrix3f dest) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + float rm11 = cos; + float rm21 = -sin; + float rm12 = sin; + float rm22 = cos; + + // add temporaries for dependent values + float nm10 = m10 * rm11 + m20 * rm12; + float nm11 = m11 * rm11 + m21 * rm12; + float nm12 = m12 * rm11 + m22 * rm12; + // set non-dependent values directly + dest.m20 = m10 * rm21 + m20 * rm22; + dest.m21 = m11 * rm21 + m21 * rm22; + dest.m22 = m12 * rm21 + m22 * rm22; + // set other values + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + return dest; + } + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3f rotateX(float ang) { + return rotateX(ang, this); + } + + public Matrix3f rotateY(float ang, Matrix3f dest) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + float rm00 = cos; + float rm20 = sin; + float rm02 = -sin; + float rm22 = cos; + + // add temporaries for dependent values + float nm00 = m00 * rm00 + m20 * rm02; + float nm01 = m01 * rm00 + m21 * rm02; + float nm02 = m02 * rm00 + m22 * rm02; + // set non-dependent values directly + dest.m20 = m00 * rm20 + m20 * rm22; + dest.m21 = m01 * rm20 + m21 * rm22; + dest.m22 = m02 * rm20 + m22 * rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + return dest; + } + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3f rotateY(float ang) { + return rotateY(ang, this); + } + + public Matrix3f rotateZ(float ang, Matrix3f dest) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + float rm00 = cos; + float rm10 = -sin; + float rm01 = sin; + float rm11 = cos; + + // add temporaries for dependent values + float nm00 = m00 * rm00 + m10 * rm01; + float nm01 = m01 * rm00 + m11 * rm01; + float nm02 = m02 * rm00 + m12 * rm01; + // set non-dependent values directly + dest.m10 = m00 * rm10 + m10 * rm11; + dest.m11 = m01 * rm10 + m11 * rm11; + dest.m12 = m02 * rm10 + m12 * rm11; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + return dest; + } + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3f rotateZ(float ang) { + return rotateZ(ang, this); + } + + /** + * Apply rotation of angles.x radians about the X axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angles.x).rotateY(angles.y).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix3f rotateXYZ(Vector3f angles) { + return rotateXYZ(angles.x, angles.y, angles.z); + } + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix3f rotateXYZ(float angleX, float angleY, float angleZ) { + return rotateXYZ(angleX, angleY, angleZ, this); + } + + public Matrix3f rotateXYZ(float angleX, float angleY, float angleZ, Matrix3f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinX = -sinX; + float m_sinY = -sinY; + float m_sinZ = -sinZ; + + // rotateX + float nm10 = m10 * cosX + m20 * sinX; + float nm11 = m11 * cosX + m21 * sinX; + float nm12 = m12 * cosX + m22 * sinX; + float nm20 = m10 * m_sinX + m20 * cosX; + float nm21 = m11 * m_sinX + m21 * cosX; + float nm22 = m12 * m_sinX + m22 * cosX; + // rotateY + float nm00 = m00 * cosY + nm20 * m_sinY; + float nm01 = m01 * cosY + nm21 * m_sinY; + float nm02 = m02 * cosY + nm22 * m_sinY; + dest.m20 = m00 * sinY + nm20 * cosY; + dest.m21 = m01 * sinY + nm21 * cosY; + dest.m22 = m02 * sinY + nm22 * cosY; + // rotateZ + dest.m00 = nm00 * cosZ + nm10 * sinZ; + dest.m01 = nm01 * cosZ + nm11 * sinZ; + dest.m02 = nm02 * cosZ + nm12 * sinZ; + dest.m10 = nm00 * m_sinZ + nm10 * cosZ; + dest.m11 = nm01 * m_sinZ + nm11 * cosZ; + dest.m12 = nm02 * m_sinZ + nm12 * cosZ; + return dest; + } + + /** + * Apply rotation of angles.z radians about the Z axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.x radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angles.z).rotateY(angles.y).rotateX(angles.x) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix3f rotateZYX(Vector3f angles) { + return rotateZYX(angles.z, angles.y, angles.x); + } + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix3f rotateZYX(float angleZ, float angleY, float angleX) { + return rotateZYX(angleZ, angleY, angleX, this); + } + + public Matrix3f rotateZYX(float angleZ, float angleY, float angleX, Matrix3f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinZ = -sinZ; + float m_sinY = -sinY; + float m_sinX = -sinX; + + // rotateZ + float nm00 = m00 * cosZ + m10 * sinZ; + float nm01 = m01 * cosZ + m11 * sinZ; + float nm02 = m02 * cosZ + m12 * sinZ; + float nm10 = m00 * m_sinZ + m10 * cosZ; + float nm11 = m01 * m_sinZ + m11 * cosZ; + float nm12 = m02 * m_sinZ + m12 * cosZ; + // rotateY + float nm20 = nm00 * sinY + m20 * cosY; + float nm21 = nm01 * sinY + m21 * cosY; + float nm22 = nm02 * sinY + m22 * cosY; + dest.m00 = nm00 * cosY + m20 * m_sinY; + dest.m01 = nm01 * cosY + m21 * m_sinY; + dest.m02 = nm02 * cosY + m22 * m_sinY; + // rotateX + dest.m10 = nm10 * cosX + nm20 * sinX; + dest.m11 = nm11 * cosX + nm21 * sinX; + dest.m12 = nm12 * cosX + nm22 * sinX; + dest.m20 = nm10 * m_sinX + nm20 * cosX; + dest.m21 = nm11 * m_sinX + nm21 * cosX; + dest.m22 = nm12 * m_sinX + nm22 * cosX; + return dest; + } + + /** + * Apply rotation of angles.y radians about the Y axis, followed by a rotation of angles.x radians about the X axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix3f rotateYXZ(Vector3f angles) { + return rotateYXZ(angles.y, angles.x, angles.z); + } + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix3f rotateYXZ(float angleY, float angleX, float angleZ) { + return rotateYXZ(angleY, angleX, angleZ, this); + } + + public Matrix3f rotateYXZ(float angleY, float angleX, float angleZ, Matrix3f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinY = -sinY; + float m_sinX = -sinX; + float m_sinZ = -sinZ; + + // rotateY + float nm20 = m00 * sinY + m20 * cosY; + float nm21 = m01 * sinY + m21 * cosY; + float nm22 = m02 * sinY + m22 * cosY; + float nm00 = m00 * cosY + m20 * m_sinY; + float nm01 = m01 * cosY + m21 * m_sinY; + float nm02 = m02 * cosY + m22 * m_sinY; + // rotateX + float nm10 = m10 * cosX + nm20 * sinX; + float nm11 = m11 * cosX + nm21 * sinX; + float nm12 = m12 * cosX + nm22 * sinX; + dest.m20 = m10 * m_sinX + nm20 * cosX; + dest.m21 = m11 * m_sinX + nm21 * cosX; + dest.m22 = m12 * m_sinX + nm22 * cosX; + // rotateZ + dest.m00 = nm00 * cosZ + nm10 * sinZ; + dest.m01 = nm01 * cosZ + nm11 * sinZ; + dest.m02 = nm02 * cosZ + nm12 * sinZ; + dest.m10 = nm00 * m_sinZ + nm10 * cosZ; + dest.m11 = nm01 * m_sinZ + nm11 * cosZ; + dest.m12 = nm02 * m_sinZ + nm12 * cosZ; + return dest; + } + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the given axis specified as x, y and z components. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix3f rotate(float ang, float x, float y, float z) { + return rotate(ang, x, y, z, this); + } + + public Matrix3f rotate(float ang, float x, float y, float z, Matrix3f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + + // rotation matrix elements: + // m30, m31, m32, m03, m13, m23 = 0 + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float rm00 = xx * C + c; + float rm01 = xy * C + z * s; + float rm02 = xz * C - y * s; + float rm10 = xy * C - z * s; + float rm11 = yy * C + c; + float rm12 = yz * C + x * s; + float rm20 = xz * C + y * s; + float rm21 = yz * C - x * s; + float rm22 = zz * C + c; + + // add temporaries for dependent values + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + // set non-dependent values directly + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotateLocal(float ang, float x, float y, float z, Matrix3f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float lm00 = xx * C + c; + float lm01 = xy * C + z * s; + float lm02 = xz * C - y * s; + float lm10 = xy * C - z * s; + float lm11 = yy * C + c; + float lm12 = yz * C + x * s; + float lm20 = xz * C + y * s; + float lm21 = yz * C - x * s; + float lm22 = zz * C + c; + float nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + float nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + float nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + float nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + float nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + float nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + float nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + float nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + float nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix3f rotateLocal(float ang, float x, float y, float z) { + return rotateLocal(ang, x, y, z, this); + } + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(float) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(float) + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotateLocalX(float ang, Matrix3f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm01 = cos * m01 - sin * m02; + float nm02 = sin * m01 + cos * m02; + float nm11 = cos * m11 - sin * m12; + float nm12 = sin * m11 + cos * m12; + float nm21 = cos * m21 - sin * m22; + float nm22 = sin * m21 + cos * m22; + dest.m00 = m00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = m10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = m20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(float) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(float) + * + * @param ang + * the angle in radians to rotate about the X axis + * @return this + */ + public Matrix3f rotateLocalX(float ang) { + return rotateLocalX(ang, this); + } + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotateLocalY(float ang, Matrix3f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm00 = cos * m00 + sin * m02; + float nm02 = -sin * m00 + cos * m02; + float nm10 = cos * m10 + sin * m12; + float nm12 = -sin * m10 + cos * m12; + float nm20 = cos * m20 + sin * m22; + float nm22 = -sin * m20 + cos * m22; + dest.m00 = nm00; + dest.m01 = m01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = m11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = m21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @return this + */ + public Matrix3f rotateLocalY(float ang) { + return rotateLocalY(ang, this); + } + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(float) rotationZ()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationZ(float) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotateLocalZ(float ang, Matrix3f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm00 = cos * m00 - sin * m01; + float nm01 = sin * m00 + cos * m01; + float nm10 = cos * m10 - sin * m11; + float nm11 = sin * m10 + cos * m11; + float nm20 = cos * m20 - sin * m21; + float nm21 = sin * m20 + cos * m21; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = m02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = m12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = m22; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @return this + */ + public Matrix3f rotateLocalZ(float ang) { + return rotateLocalZ(ang, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix3f rotate(Quaternionfc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotate(Quaternionfc quat, Matrix3f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = dxy - dzw; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotateLocal(Quaternionfc quat, Matrix3f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float lm00 = w2 + x2 - z2 - y2; + float lm01 = dxy + dzw; + float lm02 = dxz - dyw; + float lm10 = dxy - dzw; + float lm11 = y2 - z2 + w2 - x2; + float lm12 = dyz + dxw; + float lm20 = dyw + dxz; + float lm21 = dyz - dxw; + float lm22 = z2 - y2 - x2 + w2; + float nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + float nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + float nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + float nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + float nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + float nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + float nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + float nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + float nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix3f rotateLocal(Quaternionfc quat) { + return rotateLocal(quat, this); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f}, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix3f rotate(AxisAngle4f axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotate(AxisAngle4f axisAngle, Matrix3f dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(float, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix3f rotate(float angle, Vector3fc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(float, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotate(float angle, Vector3fc axis, Matrix3f dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3fc, Vector3fc) setLookAlong()}. + * + * @see #lookAlong(float, float, float, float, float, float) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix3f lookAlong(Vector3fc dir, Vector3fc up) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3fc, Vector3fc) setLookAlong()}. + * + * @see #lookAlong(float, float, float, float, float, float) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f lookAlong(Vector3fc dir, Vector3fc up, Matrix3f dest) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(float, float, float, float, float, float) setLookAlong()} + * + * @see #setLookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f lookAlong(float dirX, float dirY, float dirZ, + float upX, float upY, float upZ, Matrix3f dest) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + float rm00 = leftX; + float rm01 = upnX; + float rm02 = dirX; + float rm10 = leftY; + float rm11 = upnY; + float rm12 = dirY; + float rm20 = leftZ; + float rm21 = upnZ; + float rm22 = dirZ; + + // perform optimized matrix multiplication + // introduce temporaries for dependent results + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set the rest of the matrix elements + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(float, float, float, float, float, float) setLookAlong()} + * + * @see #setLookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix3f lookAlong(float dirX, float dirY, float dirZ, + float upX, float upY, float upZ) { + return lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(Vector3fc, Vector3fc)}. + * + * @see #setLookAlong(Vector3fc, Vector3fc) + * @see #lookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix3f setLookAlong(Vector3fc dir, Vector3fc up) { + return setLookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(float, float, float, float, float, float) lookAlong()} + * + * @see #setLookAlong(float, float, float, float, float, float) + * @see #lookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix3f setLookAlong(float dirX, float dirY, float dirZ, + float upX, float upY, float upZ) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + m00 = leftX; + m01 = upnX; + m02 = dirX; + m10 = leftY; + m11 = upnY; + m12 = dirY; + m20 = leftZ; + m21 = upnZ; + m22 = dirZ; + + return this; + } + + public Vector3f getRow(int row, Vector3f dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + return dest.set(m00, m10, m20); + case 1: + return dest.set(m01, m11, m21); + case 2: + return dest.set(m02, m12, m22); + default: + throw new IndexOutOfBoundsException(); + } + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param src + * the row components to set + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + public Matrix3f setRow(int row, Vector3fc src) throws IndexOutOfBoundsException { + return setRow(row, src.x(), src.y(), src.z()); + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param x + * the first element in the row + * @param y + * the second element in the row + * @param z + * the third element in the row + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + public Matrix3f setRow(int row, float x, float y, float z) throws IndexOutOfBoundsException { + switch (row) { + case 0: + this.m00 = x; + this.m10 = y; + this.m20 = z; + break; + case 1: + this.m01 = x; + this.m11 = y; + this.m21 = z; + break; + case 2: + this.m02 = x; + this.m12 = y; + this.m22 = z; + break; + default: + throw new IndexOutOfBoundsException(); + } + return this; + } + + public Vector3f getColumn(int column, Vector3f dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + return dest.set(m00, m01, m02); + case 1: + return dest.set(m10, m11, m12); + case 2: + return dest.set(m20, m21, m22); + default: + throw new IndexOutOfBoundsException(); + } + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..2] + * @param src + * the column components to set + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..2] + */ + public Matrix3f setColumn(int column, Vector3fc src) throws IndexOutOfBoundsException { + return setColumn(column, src.x(), src.y(), src.z()); + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..2] + * @param x + * the first element in the column + * @param y + * the second element in the column + * @param z + * the third element in the column + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..2] + */ + public Matrix3f setColumn(int column, float x, float y, float z) throws IndexOutOfBoundsException { + switch (column) { + case 0: + this.m00 = x; + this.m01 = y; + this.m02 = z; + break; + case 1: + this.m10 = x; + this.m11 = y; + this.m12 = z; + break; + case 2: + this.m20 = x; + this.m21 = y; + this.m22 = z; + break; + default: + throw new IndexOutOfBoundsException(); + } + return this; + } + + public float get(int column, int row) { + return MemUtil.INSTANCE.get(this, column, row); + } + + /** + * Set the matrix element at the given column and row to the specified value. + * + * @param column + * the colum index in [0..2] + * @param row + * the row index in [0..2] + * @param value + * the value + * @return this + */ + public Matrix3f set(int column, int row, float value) { + return MemUtil.INSTANCE.set(this, column, row, value); + } + + public float getRowColumn(int row, int column) { + return MemUtil.INSTANCE.get(this, column, row); + } + + /** + * Set the matrix element at the given row and column to the specified value. + * + * @param row + * the row index in [0..2] + * @param column + * the colum index in [0..2] + * @param value + * the value + * @return this + */ + public Matrix3f setRowColumn(int row, int column, float value) { + return MemUtil.INSTANCE.set(this, column, row, value); + } + + /** + * Set this matrix to its own normal matrix. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In this case, use {@link #set(Matrix3fc)} to set a given Matrix3f to this matrix. + * + * @see #set(Matrix3fc) + * + * @return this + */ + public Matrix3f normal() { + return normal(this); + } + + /** + * Compute a normal matrix from this matrix and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In this case, use {@link #set(Matrix3fc)} to set a given Matrix3f to this matrix. + * + * @see #set(Matrix3fc) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f normal(Matrix3f dest) { + float m00m11 = m00 * m11; + float m01m10 = m01 * m10; + float m02m10 = m02 * m10; + float m00m12 = m00 * m12; + float m01m12 = m01 * m12; + float m02m11 = m02 * m11; + float det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + float s = 1.0f / det; + /* Invert and transpose in one go */ + float nm00 = (m11 * m22 - m21 * m12) * s; + float nm01 = (m20 * m12 - m10 * m22) * s; + float nm02 = (m10 * m21 - m20 * m11) * s; + float nm10 = (m21 * m02 - m01 * m22) * s; + float nm11 = (m00 * m22 - m20 * m02) * s; + float nm12 = (m20 * m01 - m00 * m21) * s; + float nm20 = (m01m12 - m02m11) * s; + float nm21 = (m02m10 - m00m12) * s; + float nm22 = (m00m11 - m01m10) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + /** + * Compute the cofactor matrix of this. + *

+ * The cofactor matrix can be used instead of {@link #normal()} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @return this + */ + public Matrix3f cofactor() { + return cofactor(this); + } + + /** + * Compute the cofactor matrix of this and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f cofactor(Matrix3f dest) { + float nm00 = m11 * m22 - m21 * m12; + float nm01 = m20 * m12 - m10 * m22; + float nm02 = m10 * m21 - m20 * m11; + float nm10 = m21 * m02 - m01 * m22; + float nm11 = m00 * m22 - m20 * m02; + float nm12 = m20 * m01 - m00 * m21; + float nm20 = m01 * m12 - m11 * m02; + float nm21 = m02 * m10 - m12 * m00; + float nm22 = m00 * m11 - m10 * m01; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + return dest; + } + + public Vector3f getScale(Vector3f dest) { + return dest.set(Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02), + Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12), + Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22)); + } + + public Vector3f positiveZ(Vector3f dir) { + dir.x = m10 * m21 - m11 * m20; + dir.y = m20 * m01 - m21 * m00; + dir.z = m00 * m11 - m01 * m10; + return dir.normalize(dir); + } + + public Vector3f normalizedPositiveZ(Vector3f dir) { + dir.x = m02; + dir.y = m12; + dir.z = m22; + return dir; + } + + public Vector3f positiveX(Vector3f dir) { + dir.x = m11 * m22 - m12 * m21; + dir.y = m02 * m21 - m01 * m22; + dir.z = m01 * m12 - m02 * m11; + return dir.normalize(dir); + } + + public Vector3f normalizedPositiveX(Vector3f dir) { + dir.x = m00; + dir.y = m10; + dir.z = m20; + return dir; + } + + public Vector3f positiveY(Vector3f dir) { + dir.x = m12 * m20 - m10 * m22; + dir.y = m00 * m22 - m02 * m20; + dir.z = m02 * m10 - m00 * m12; + return dir.normalize(dir); + } + + public Vector3f normalizedPositiveY(Vector3f dir) { + dir.x = m01; + dir.y = m11; + dir.z = m21; + return dir; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(m00); + result = prime * result + Float.floatToIntBits(m01); + result = prime * result + Float.floatToIntBits(m02); + result = prime * result + Float.floatToIntBits(m10); + result = prime * result + Float.floatToIntBits(m11); + result = prime * result + Float.floatToIntBits(m12); + result = prime * result + Float.floatToIntBits(m20); + result = prime * result + Float.floatToIntBits(m21); + result = prime * result + Float.floatToIntBits(m22); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Matrix3f other = (Matrix3f) obj; + if (Float.floatToIntBits(m00) != Float.floatToIntBits(other.m00)) + return false; + if (Float.floatToIntBits(m01) != Float.floatToIntBits(other.m01)) + return false; + if (Float.floatToIntBits(m02) != Float.floatToIntBits(other.m02)) + return false; + if (Float.floatToIntBits(m10) != Float.floatToIntBits(other.m10)) + return false; + if (Float.floatToIntBits(m11) != Float.floatToIntBits(other.m11)) + return false; + if (Float.floatToIntBits(m12) != Float.floatToIntBits(other.m12)) + return false; + if (Float.floatToIntBits(m20) != Float.floatToIntBits(other.m20)) + return false; + if (Float.floatToIntBits(m21) != Float.floatToIntBits(other.m21)) + return false; + if (Float.floatToIntBits(m22) != Float.floatToIntBits(other.m22)) + return false; + return true; + } + + public boolean equals(Matrix3fc m, float delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix3f)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m02, m.m02(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + if (!Runtime.equals(m12, m.m12(), delta)) + return false; + if (!Runtime.equals(m20, m.m20(), delta)) + return false; + if (!Runtime.equals(m21, m.m21(), delta)) + return false; + if (!Runtime.equals(m22, m.m22(), delta)) + return false; + return true; + } + + /** + * Exchange the values of this matrix with the given other matrix. + * + * @param other + * the other matrix to exchange the values with + * @return this + */ + public Matrix3f swap(Matrix3f other) { + MemUtil.INSTANCE.swap(this, other); + return this; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix3f add(Matrix3fc other) { + return add(other, this); + } + + public Matrix3f add(Matrix3fc other, Matrix3f dest) { + dest.m00 = m00 + other.m00(); + dest.m01 = m01 + other.m01(); + dest.m02 = m02 + other.m02(); + dest.m10 = m10 + other.m10(); + dest.m11 = m11 + other.m11(); + dest.m12 = m12 + other.m12(); + dest.m20 = m20 + other.m20(); + dest.m21 = m21 + other.m21(); + dest.m22 = m22 + other.m22(); + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix3f sub(Matrix3fc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix3f sub(Matrix3fc subtrahend, Matrix3f dest) { + dest.m00 = m00 - subtrahend.m00(); + dest.m01 = m01 - subtrahend.m01(); + dest.m02 = m02 - subtrahend.m02(); + dest.m10 = m10 - subtrahend.m10(); + dest.m11 = m11 - subtrahend.m11(); + dest.m12 = m12 - subtrahend.m12(); + dest.m20 = m20 - subtrahend.m20(); + dest.m21 = m21 - subtrahend.m21(); + dest.m22 = m22 - subtrahend.m22(); + return dest; + } + + /** + * Component-wise multiply this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix3f mulComponentWise(Matrix3fc other) { + return mulComponentWise(other, this); + } + + public Matrix3f mulComponentWise(Matrix3fc other, Matrix3f dest) { + dest.m00 = m00 * other.m00(); + dest.m01 = m01 * other.m01(); + dest.m02 = m02 * other.m02(); + dest.m10 = m10 * other.m10(); + dest.m11 = m11 * other.m11(); + dest.m12 = m12 * other.m12(); + dest.m20 = m20 * other.m20(); + dest.m21 = m21 * other.m21(); + dest.m22 = m22 * other.m22(); + return dest; + } + + /** + * Set this matrix to a skew-symmetric matrix using the following layout: + *

+     *  0,  a, -b
+     * -a,  0,  c
+     *  b, -c,  0
+     * 
+ * + * Reference: https://en.wikipedia.org + * + * @param a + * the value used for the matrix elements m01 and m10 + * @param b + * the value used for the matrix elements m02 and m20 + * @param c + * the value used for the matrix elements m12 and m21 + * @return this + */ + public Matrix3f setSkewSymmetric(float a, float b, float c) { + m00 = m11 = m22 = 0; + m01 = -a; + m02 = b; + m10 = a; + m12 = -c; + m20 = -b; + m21 = c; + return this; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Matrix3f lerp(Matrix3fc other, float t) { + return lerp(other, t, this); + } + + public Matrix3f lerp(Matrix3fc other, float t, Matrix3f dest) { + dest.m00 = Math.fma(other.m00() - m00, t, m00); + dest.m01 = Math.fma(other.m01() - m01, t, m01); + dest.m02 = Math.fma(other.m02() - m02, t, m02); + dest.m10 = Math.fma(other.m10() - m10, t, m10); + dest.m11 = Math.fma(other.m11() - m11, t, m11); + dest.m12 = Math.fma(other.m12() - m12, t, m12); + dest.m20 = Math.fma(other.m20() - m20, t, m20); + dest.m21 = Math.fma(other.m21() - m21, t, m21); + dest.m22 = Math.fma(other.m22() - m22, t, m22); + return dest; + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3fc, Vector3fc) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix3f().lookAlong(new Vector3f(dir).negate(), up).invert(), dest) + * + * @see #rotateTowards(float, float, float, float, float, float, Matrix3f) + * @see #rotationTowards(Vector3fc, Vector3fc) + * + * @param direction + * the direction to rotate towards + * @param up + * the model's up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotateTowards(Vector3fc direction, Vector3fc up, Matrix3f dest) { + return rotateTowards(direction.x(), direction.y(), direction.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3fc, Vector3fc) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix3f().lookAlong(new Vector3f(dir).negate(), up).invert()) + * + * @see #rotateTowards(float, float, float, float, float, float) + * @see #rotationTowards(Vector3fc, Vector3fc) + * + * @param direction + * the direction to orient towards + * @param up + * the up vector + * @return this + */ + public Matrix3f rotateTowards(Vector3fc direction, Vector3fc up) { + return rotateTowards(direction.x(), direction.y(), direction.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(float, float, float, float, float, float) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix3f().lookAlong(-dirX, -dirY, -dirZ, upX, upY, upZ).invert()) + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix3f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + return rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(float, float, float, float, float, float) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix3f().lookAlong(-dirX, -dirY, -dirZ, upX, upY, upZ).invert(), dest) + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix3f dest) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float ndirX = dirX * invDirLength; + float ndirY = dirY * invDirLength; + float ndirZ = dirZ * invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = ndirY * leftZ - ndirZ * leftY; + float upnY = ndirZ * leftX - ndirX * leftZ; + float upnZ = ndirX * leftY - ndirY * leftX; + float rm00 = leftX; + float rm01 = leftY; + float rm02 = leftZ; + float rm10 = upnX; + float rm11 = upnY; + float rm12 = upnZ; + float rm20 = ndirX; + float rm21 = ndirY; + float rm22 = ndirZ; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + return dest; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with center - eye. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(float, float, float, float, float, float) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAlong(new Vector3f(dir).negate(), up).invert() + * + * @see #rotationTowards(Vector3fc, Vector3fc) + * @see #rotateTowards(float, float, float, float, float, float) + * + * @param dir + * the direction to orient the local -z axis towards + * @param up + * the up vector + * @return this + */ + public Matrix3f rotationTowards(Vector3fc dir, Vector3fc up) { + return rotationTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with center - eye. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(float, float, float, float, float, float) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAlong(-dirX, -dirY, -dirZ, upX, upY, upZ).invert() + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix3f rotationTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float ndirX = dirX * invDirLength; + float ndirY = dirY * invDirLength; + float ndirZ = dirZ * invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = ndirY * leftZ - ndirZ * leftY; + float upnY = ndirZ * leftX - ndirX * leftZ; + float upnZ = ndirX * leftY - ndirY * leftX; + this.m00 = leftX; + this.m01 = leftY; + this.m02 = leftZ; + this.m10 = upnX; + this.m11 = upnY; + this.m12 = upnZ; + this.m20 = ndirX; + this.m21 = ndirY; + this.m22 = ndirZ; + return this; + } + + public Vector3f getEulerAnglesZYX(Vector3f dest) { + dest.x = Math.atan2(m12, m22); + dest.y = Math.atan2(-m02, Math.sqrt(1.0f - m02 * m02)); + dest.z = Math.atan2(m01, m00); + return dest; + } + + public Vector3f getEulerAnglesXYZ(Vector3f dest) { + dest.x = Math.atan2(-m21, m22); + dest.y = Math.atan2(m20, Math.sqrt(1.0f - m20 * m20)); + dest.z = Math.atan2(-m10, m00); + return dest; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a
+     * 0 1 b
+     * 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @return this + */ + public Matrix3f obliqueZ(float a, float b) { + this.m20 = m00 * a + m10 * b + m20; + this.m21 = m01 * a + m11 * b + m21; + this.m22 = m02 * a + m12 * b + m22; + return this; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a
+     * 0 1 b
+     * 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f obliqueZ(float a, float b, Matrix3f dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m00 * a + m10 * b + m20; + dest.m21 = m01 * a + m11 * b + m21; + dest.m22 = m02 * a + m12 * b + m22; + return dest; + } + + public Matrix3f reflect(float nx, float ny, float nz, Matrix3f dest) { + float da = nx + nx, db = ny + ny, dc = nz + nz; + float rm00 = 1.0f - da * nx; + float rm01 = -da * ny; + float rm02 = -da * nz; + float rm10 = -db * nx; + float rm11 = 1.0f - db * ny; + float rm12 = -db * nz; + float rm20 = -dc * nx; + float rm21 = -dc * ny; + float rm22 = 1.0f - dc * nz; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + return dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through the given plane + * specified via the plane normal. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @return this + */ + public Matrix3f reflect(float nx, float ny, float nz) { + return reflect(nx, ny, nz, this); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through the given plane + * specified via the plane normal. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @return this + */ + public Matrix3f reflect(Vector3fc normal) { + return reflect(normal.x(), normal.y(), normal.z()); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation + * @return this + */ + public Matrix3f reflect(Quaternionfc orientation) { + return reflect(orientation, this); + } + + public Matrix3f reflect(Quaternionfc orientation, Matrix3f dest) { + float num1 = orientation.x() + orientation.x(); + float num2 = orientation.y() + orientation.y(); + float num3 = orientation.z() + orientation.z(); + float normalX = orientation.x() * num3 + orientation.w() * num2; + float normalY = orientation.y() * num3 - orientation.w() * num1; + float normalZ = 1.0f - (orientation.x() * num1 + orientation.y() * num2); + return reflect(normalX, normalY, normalZ, dest); + } + + public Matrix3f reflect(Vector3fc normal, Matrix3f dest) { + return reflect(normal.x(), normal.y(), normal.z(), dest); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects through the given plane + * specified via the plane normal. + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @return this + */ + public Matrix3f reflection(float nx, float ny, float nz) { + float da = nx + nx, db = ny + ny, dc = nz + nz; + this._m00(1.0f - da * nx); + this._m01(-da * ny); + this._m02(-da * nz); + this._m10(-db * nx); + this._m11(1.0f - db * ny); + this._m12(-db * nz); + this._m20(-dc * nx); + this._m21(-dc * ny); + this._m22(1.0f - dc * nz); + return this; + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects through the given plane + * specified via the plane normal. + * + * @param normal + * the plane normal + * @return this + */ + public Matrix3f reflection(Vector3fc normal) { + return reflection(normal.x(), normal.y(), normal.z()); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects through a plane + * specified via the plane orientation. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + * + * @param orientation + * the plane orientation + * @return this + */ + public Matrix3f reflection(Quaternionfc orientation) { + float num1 = orientation.x() + orientation.x(); + float num2 = orientation.y() + orientation.y(); + float num3 = orientation.z() + orientation.z(); + float normalX = orientation.x() * num3 + orientation.w() * num2; + float normalY = orientation.y() * num3 - orientation.w() * num1; + float normalZ = 1.0f - (orientation.x() * num1 + orientation.y() * num2); + return reflection(normalX, normalY, normalZ); + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && + Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && + Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22); + } + + public float quadraticFormProduct(float x, float y, float z) { + float Axx = m00 * x + m10 * y + m20 * z; + float Axy = m01 * x + m11 * y + m21 * z; + float Axz = m02 * x + m12 * y + m22 * z; + return x * Axx + y * Axy + z * Axz; + } + + public float quadraticFormProduct(Vector3fc v) { + return quadraticFormProduct(v.x(), v.y(), v.z()); + } + + /** + * Multiply this by the matrix + *

+     * 1 0 0
+     * 0 0 1
+     * 0 1 0
+     * 
+ * + * @return this + */ + public Matrix3f mapXZY() { + return mapXZY(this); + } + public Matrix3f mapXZY(Matrix3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m20)._m11(m21)._m12(m22)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * 1 0  0
+     * 0 0 -1
+     * 0 1  0
+     * 
+ * + * @return this + */ + public Matrix3f mapXZnY() { + return mapXZnY(this); + } + public Matrix3f mapXZnY(Matrix3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m20)._m11(m21)._m12(m22)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0
+     * 0 -1  0
+     * 0  0 -1
+     * 
+ * + * @return this + */ + public Matrix3f mapXnYnZ() { + return mapXnYnZ(this); + } + public Matrix3f mapXnYnZ(Matrix3f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * 1  0 0
+     * 0  0 1
+     * 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix3f mapXnZY() { + return mapXnZY(this); + } + public Matrix3f mapXnZY(Matrix3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0
+     * 0  0 -1
+     * 0 -1  0
+     * 
+ * + * @return this + */ + public Matrix3f mapXnZnY() { + return mapXnZnY(this); + } + public Matrix3f mapXnZnY(Matrix3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0
+     * 1 0 0
+     * 0 0 1
+     * 
+ * + * @return this + */ + public Matrix3f mapYXZ() { + return mapYXZ(this); + } + public Matrix3f mapYXZ(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m00)._m11(m01)._m12(m02)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0
+     * 1 0  0
+     * 0 0 -1
+     * 
+ * + * @return this + */ + public Matrix3f mapYXnZ() { + return mapYXnZ(this); + } + public Matrix3f mapYXnZ(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m00)._m11(m01)._m12(m02)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1
+     * 1 0 0
+     * 0 1 0
+     * 
+ * + * @return this + */ + public Matrix3f mapYZX() { + return mapYZX(this); + } + public Matrix3f mapYZX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m20)._m11(m21)._m12(m22)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1
+     * 1 0  0
+     * 0 1  0
+     * 
+ * + * @return this + */ + public Matrix3f mapYZnX() { + return mapYZnX(this); + } + public Matrix3f mapYZnX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m20)._m11(m21)._m12(m22)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0
+     * 1  0 0
+     * 0  0 1
+     * 
+ * + * @return this + */ + public Matrix3f mapYnXZ() { + return mapYnXZ(this); + } + public Matrix3f mapYnXZ(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0
+     * 1  0  0
+     * 0  0 -1
+     * 
+ * + * @return this + */ + public Matrix3f mapYnXnZ() { + return mapYnXnZ(this); + } + public Matrix3f mapYnXnZ(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1
+     * 1  0 0
+     * 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix3f mapYnZX() { + return mapYnZX(this); + } + public Matrix3f mapYnZX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1
+     * 1  0  0
+     * 0 -1  0
+     * 
+ * + * @return this + */ + public Matrix3f mapYnZnX() { + return mapYnZnX(this); + } + public Matrix3f mapYnZnX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0
+     * 0 0 1
+     * 1 0 0
+     * 
+ * + * @return this + */ + public Matrix3f mapZXY() { + return mapZXY(this); + } + public Matrix3f mapZXY(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m00)._m11(m01)._m12(m02)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0
+     * 0 0 -1
+     * 1 0  0
+     * 
+ * + * @return this + */ + public Matrix3f mapZXnY() { + return mapZXnY(this); + } + public Matrix3f mapZXnY(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m00)._m11(m01)._m12(m02)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1
+     * 0 1 0
+     * 1 0 0
+     * 
+ * + * @return this + */ + public Matrix3f mapZYX() { + return mapZYX(this); + } + public Matrix3f mapZYX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m10)._m11(m11)._m12(m12)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1
+     * 0 1  0
+     * 1 0  0
+     * 
+ * + * @return this + */ + public Matrix3f mapZYnX() { + return mapZYnX(this); + } + public Matrix3f mapZYnX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m10)._m11(m11)._m12(m12)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0
+     * 0  0 1
+     * 1  0 0
+     * 
+ * + * @return this + */ + public Matrix3f mapZnXY() { + return mapZnXY(this); + } + public Matrix3f mapZnXY(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0
+     * 0  0 -1
+     * 1  0  0
+     * 
+ * + * @return this + */ + public Matrix3f mapZnXnY() { + return mapZnXnY(this); + } + public Matrix3f mapZnXnY(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1
+     * 0 -1 0
+     * 1  0 0
+     * 
+ * + * @return this + */ + public Matrix3f mapZnYX() { + return mapZnYX(this); + } + public Matrix3f mapZnYX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1
+     * 0 -1  0
+     * 1  0  0
+     * 
+ * + * @return this + */ + public Matrix3f mapZnYnX() { + return mapZnYnX(this); + } + public Matrix3f mapZnYnX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0
+     *  0 1  0
+     *  0 0 -1
+     * 
+ * + * @return this + */ + public Matrix3f mapnXYnZ() { + return mapnXYnZ(this); + } + public Matrix3f mapnXYnZ(Matrix3f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m10)._m11(m11)._m12(m12)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * -1 0 0
+     *  0 0 1
+     *  0 1 0
+     * 
+ * + * @return this + */ + public Matrix3f mapnXZY() { + return mapnXZY(this); + } + public Matrix3f mapnXZY(Matrix3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m20)._m11(m21)._m12(m22)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0
+     *  0 0 -1
+     *  0 1  0
+     * 
+ * + * @return this + */ + public Matrix3f mapnXZnY() { + return mapnXZnY(this); + } + public Matrix3f mapnXZnY(Matrix3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m20)._m11(m21)._m12(m22)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0
+     *  0 -1 0
+     *  0  0 1
+     * 
+ * + * @return this + */ + public Matrix3f mapnXnYZ() { + return mapnXnYZ(this); + } + public Matrix3f mapnXnYZ(Matrix3f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0
+     *  0 -1  0
+     *  0  0 -1
+     * 
+ * + * @return this + */ + public Matrix3f mapnXnYnZ() { + return mapnXnYnZ(this); + } + public Matrix3f mapnXnYnZ(Matrix3f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0
+     *  0  0 1
+     *  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix3f mapnXnZY() { + return mapnXnZY(this); + } + public Matrix3f mapnXnZY(Matrix3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0
+     *  0  0 -1
+     *  0 -1  0
+     * 
+ * + * @return this + */ + public Matrix3f mapnXnZnY() { + return mapnXnZnY(this); + } + public Matrix3f mapnXnZnY(Matrix3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0
+     * -1 0 0
+     *  0 0 1
+     * 
+ * + * @return this + */ + public Matrix3f mapnYXZ() { + return mapnYXZ(this); + } + public Matrix3f mapnYXZ(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m00)._m11(m01)._m12(m02)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0
+     * -1 0  0
+     *  0 0 -1
+     * 
+ * + * @return this + */ + public Matrix3f mapnYXnZ() { + return mapnYXnZ(this); + } + public Matrix3f mapnYXnZ(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m00)._m11(m01)._m12(m02)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1
+     * -1 0 0
+     *  0 1 0
+     * 
+ * + * @return this + */ + public Matrix3f mapnYZX() { + return mapnYZX(this); + } + public Matrix3f mapnYZX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m20)._m11(m21)._m12(m22)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1
+     * -1 0  0
+     *  0 1  0
+     * 
+ * + * @return this + */ + public Matrix3f mapnYZnX() { + return mapnYZnX(this); + } + public Matrix3f mapnYZnX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m20)._m11(m21)._m12(m22)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0
+     * -1  0 0
+     *  0  0 1
+     * 
+ * + * @return this + */ + public Matrix3f mapnYnXZ() { + return mapnYnXZ(this); + } + public Matrix3f mapnYnXZ(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m20)._m21(m21)._m22(m22); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0
+     * -1  0  0
+     *  0  0 -1
+     * 
+ * + * @return this + */ + public Matrix3f mapnYnXnZ() { + return mapnYnXnZ(this); + } + public Matrix3f mapnYnXnZ(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m20)._m21(-m21)._m22(-m22); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1
+     * -1  0 0
+     *  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix3f mapnYnZX() { + return mapnYnZX(this); + } + public Matrix3f mapnYnZX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1
+     * -1  0  0
+     *  0 -1  0
+     * 
+ * + * @return this + */ + public Matrix3f mapnYnZnX() { + return mapnYnZnX(this); + } + public Matrix3f mapnYnZnX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0
+     *  0 0 1
+     * -1 0 0
+     * 
+ * + * @return this + */ + public Matrix3f mapnZXY() { + return mapnZXY(this); + } + public Matrix3f mapnZXY(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m00)._m11(m01)._m12(m02)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0
+     *  0 0 -1
+     * -1 0  0
+     * 
+ * + * @return this + */ + public Matrix3f mapnZXnY() { + return mapnZXnY(this); + } + public Matrix3f mapnZXnY(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m00)._m11(m01)._m12(m02)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1
+     *  0 1 0
+     * -1 0 0
+     * 
+ * + * @return this + */ + public Matrix3f mapnZYX() { + return mapnZYX(this); + } + public Matrix3f mapnZYX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m10)._m11(m11)._m12(m12)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1
+     *  0 1  0
+     * -1 0  0
+     * 
+ * + * @return this + */ + public Matrix3f mapnZYnX() { + return mapnZYnX(this); + } + public Matrix3f mapnZYnX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m10)._m11(m11)._m12(m12)._m20(-m00)._m21(-m01)._m22(-m02); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0
+     *  0  0 1
+     * -1  0 0
+     * 
+ * + * @return this + */ + public Matrix3f mapnZnXY() { + return mapnZnXY(this); + } + public Matrix3f mapnZnXY(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m10)._m21(m11)._m22(m12); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0
+     *  0  0 -1
+     * -1  0  0
+     * 
+ * + * @return this + */ + public Matrix3f mapnZnXnY() { + return mapnZnXnY(this); + } + public Matrix3f mapnZnXnY(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m10)._m21(-m11)._m22(-m12); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1
+     *  0 -1 0
+     * -1  0 0
+     * 
+ * + * @return this + */ + public Matrix3f mapnZnYX() { + return mapnZnYX(this); + } + public Matrix3f mapnZnYX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m00)._m21(m01)._m22(m02); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1
+     *  0 -1  0
+     * -1  0  0
+     * 
+ * + * @return this + */ + public Matrix3f mapnZnYnX() { + return mapnZnYnX(this); + } + public Matrix3f mapnZnYnX(Matrix3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m00)._m21(-m01)._m22(-m02); + } + + /** + * Multiply this by the matrix + *
+     * -1 0 0
+     *  0 1 0
+     *  0 0 1
+     * 
+ * + * @return this + */ + public Matrix3f negateX() { + return _m00(-m00)._m01(-m01)._m02(-m02); + } + public Matrix3f negateX(Matrix3f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m10)._m11(m11)._m12(m12)._m20(m20)._m21(m21)._m22(m22); + } + + /** + * Multiply this by the matrix + *
+     * 1  0 0
+     * 0 -1 0
+     * 0  0 1
+     * 
+ * + * @return this + */ + public Matrix3f negateY() { + return _m10(-m10)._m11(-m11)._m12(-m12); + } + public Matrix3f negateY(Matrix3f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m20)._m21(m21)._m22(m22); + } + + /** + * Multiply this by the matrix + *
+     * 1 0  0
+     * 0 1  0
+     * 0 0 -1
+     * 
+ * + * @return this + */ + public Matrix3f negateZ() { + return _m20(-m20)._m21(-m21)._m22(-m22); + } + public Matrix3f negateZ(Matrix3f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m10)._m11(m11)._m12(m12)._m20(-m20)._m21(-m21)._m22(-m22); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3fStack.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3fStack.java new file mode 100644 index 000000000..8e53a5456 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3fStack.java @@ -0,0 +1,186 @@ +/* + * The MIT License + * + * Copyright (c) 2018-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A stack of many {@link Matrix3f} instances. This resembles the matrix stack known from legacy OpenGL. + *

+ * This {@link Matrix3fStack} class inherits from {@link Matrix3f}, so the current/top matrix is always the + * {@link Matrix3fStack}/{@link Matrix3f} itself. This affects all operations in {@link Matrix3f} that take another + * {@link Matrix3f} as parameter. If a {@link Matrix3fStack} is used as argument to those methods, the effective + * argument will always be the current matrix of the matrix stack. + * + * @author Kai Burjack + */ +public class Matrix3fStack extends Matrix3f { + + private static final long serialVersionUID = 1L; + + /** + * The matrix stack as a non-growable array. The size of the stack must be specified in the {@link #Matrix3fStack(int) constructor}. + */ + private Matrix3f[] mats; + + /** + * The index of the "current" matrix within {@link #mats}. + */ + private int curr; + + /** + * Create a new {@link Matrix3fStack} of the given size. + *

+ * Initially the stack pointer is at zero and the current matrix is set to identity. + * + * @param stackSize + * the size of the stack. This must be at least 1, in which case the {@link Matrix3fStack} simply only consists of this + * {@link Matrix3f} + */ + public Matrix3fStack(int stackSize) { + if (stackSize < 1) { + throw new IllegalArgumentException("stackSize must be >= 1"); //$NON-NLS-1$ + } + mats = new Matrix3f[stackSize - 1]; + // Allocate all matrices up front to keep the promise of being "allocation-free" + for (int i = 0; i < mats.length; i++) { + mats[i] = new Matrix3f(); + } + } + + /** + * Do not invoke manually! Only meant for serialization. + *

+ * Invoking this constructor from client code will result in an inconsistent state of the + * created {@link Matrix3fStack} instance. + */ + public Matrix3fStack() { + /* Empty! */ + } + + /** + * Set the stack pointer to zero and set the current/bottom matrix to {@link #identity() identity}. + * + * @return this + */ + public Matrix3fStack clear() { + curr = 0; + identity(); + return this; + } + + /** + * Increment the stack pointer by one and set the values of the new current matrix to the one directly below it. + * + * @return this + */ + public Matrix3fStack pushMatrix() { + if (curr == mats.length) { + throw new IllegalStateException("max stack size of " + (curr + 1) + " reached"); //$NON-NLS-1$ //$NON-NLS-2$ + } + mats[curr++].set(this); + return this; + } + + /** + * Decrement the stack pointer by one. + *

+ * This will effectively dispose of the current matrix. + * + * @return this + */ + public Matrix3fStack popMatrix() { + if (curr == 0) { + throw new IllegalStateException("already at the bottom of the stack"); //$NON-NLS-1$ + } + set(mats[--curr]); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + curr; + for (int i = 0; i < curr; i++) { + result = prime * result + mats[i].hashCode(); + } + return result; + } + + /* + * Contract between Matrix3f and Matrix3fStack: + * + * - Matrix3f.equals(Matrix3fStack) is true iff all the 9 matrix elements are equal + * - Matrix3fStack.equals(Matrix3f) is true iff all the 9 matrix elements are equal + * - Matrix3fStack.equals(Matrix3fStack) is true iff all 9 matrix elements are equal AND the matrix arrays as well as the stack pointer are equal + * - everything else is inequal + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (obj instanceof Matrix3fStack) { + Matrix3fStack other = (Matrix3fStack) obj; + if (curr != other.curr) + return false; + for (int i = 0; i < curr; i++) { + if (!mats[i].equals(other.mats[i])) + return false; + } + } + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(curr); + for (int i = 0; i < curr; i++) { + out.writeObject(mats[i]); + } + } + + public void readExternal(ObjectInput in) throws IOException { + super.readExternal(in); + curr = in.readInt(); + mats = new Matrix3fStack[curr]; + for (int i = 0; i < curr; i++) { + Matrix3f m = new Matrix3f(); + m.readExternal(in); + mats[i] = m; + } + } + + public Object clone() throws CloneNotSupportedException { + Matrix3fStack cloned = (Matrix3fStack) super.clone(); + Matrix3f[] clonedMats = new Matrix3f[mats.length]; + for (int i = 0; i < mats.length; i++) + clonedMats[i] = (Matrix3f) mats[i].clone(); + cloned.mats = clonedMats; + return cloned; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3fc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3fc.java new file mode 100644 index 000000000..9ffaf1905 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3fc.java @@ -0,0 +1,2199 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + + +/** + * Interface to a read-only view of a 3x3 matrix of single-precision floats. + * + * @author Kai Burjack + */ +public interface Matrix3fc { + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + float m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + float m01(); + + /** + * Return the value of the matrix element at column 0 and row 2. + * + * @return the value of the matrix element + */ + float m02(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + float m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + float m11(); + + /** + * Return the value of the matrix element at column 1 and row 2. + * + * @return the value of the matrix element + */ + float m12(); + + /** + * Return the value of the matrix element at column 2 and row 0. + * + * @return the value of the matrix element + */ + float m20(); + + /** + * Return the value of the matrix element at column 2 and row 1. + * + * @return the value of the matrix element + */ + float m21(); + + /** + * Return the value of the matrix element at column 2 and row 2. + * + * @return the value of the matrix element + */ + float m22(); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mul(Matrix3fc right, Matrix3f dest); + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in dest. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix3f mulLocal(Matrix3fc left, Matrix3f dest); + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + float determinant(); + + /** + * Invert the this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f invert(Matrix3f dest); + + /** + * Transpose this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f transpose(Matrix3f dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3f get(Matrix3f dest); + + /** + * Get the current values of this matrix and store them as + * the rotational component of dest. All other values of dest will + * be set to identity. + * + * @see Matrix4f#set(Matrix3fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4f get(Matrix4f dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link AxisAngle4f}. + * + * @see AxisAngle4f#set(Matrix3fc) + * + * @param dest + * the destination {@link AxisAngle4f} + * @return the passed in destination + */ + AxisAngle4f getRotation(AxisAngle4f dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the three column vectors of this matrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaternionf#setFromUnnormalized(Matrix3fc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getUnnormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the three column vectors of this matrix are normalized. + * + * @see Quaternionf#setFromNormalized(Matrix3fc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getNormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the three column vectors of this matrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaterniond#setFromUnnormalized(Matrix3fc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getUnnormalizedRotation(Quaterniond dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the three column vectors of this matrix are normalized. + * + * @see Quaterniond#setFromNormalized(Matrix3fc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getNormalizedRotation(Quaterniond dest); + + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this matrix as 3x4 matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}, with the m03, m13 and m23 components being zero. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get3x4(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x4(int, FloatBuffer) + * + * @param buffer + * will receive the values of this 3x3 matrix as 3x4 matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get3x4(FloatBuffer buffer); + + /** + * Store this matrix as 3x4 matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index, with the m03, m13 and m23 components being zero. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this 3x3 matrix as 3x4 matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get3x4(int index, FloatBuffer buffer); + + /** + * Store this matrix as 3x4 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}, with the m03, m13 and m23 components being zero. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get3x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of this 3x3 matrix as 3x4 matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get3x4(ByteBuffer buffer); + + /** + * Store this matrix as 3x4 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index, with the m03, m13 and m23 components being zero. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this 3x3 matrix as 3x4 matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get3x4(int index, ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #getTransposed(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer getTransposed(FloatBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer getTransposed(int index, FloatBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getTransposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getTransposed(ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer getTransposed(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix3fc getToAddress(long address); + + /** + * Store this matrix into the supplied float array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get(float[] arr, int offset); + + /** + * Store this matrix into the supplied float array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get(float[] arr); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix3f scale(Vector3fc xyz, Matrix3f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix3f scale(float x, float y, float z, Matrix3f dest); + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(float, float, float, Matrix3f) + * + * @param xyz + * the factor for all components + * @param dest + * will hold the result + * @return dest + */ + Matrix3f scale(float xyz, Matrix3f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix3f scaleLocal(float x, float y, float z, Matrix3f dest); + + /** + * Transform the given vector by this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector3f transform(Vector3f v); + + /** + * Transform the given vector by this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transform(Vector3fc v, Vector3f dest); + + /** + * Transform the vector (x, y, z) by this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transform(float x, float y, float z, Vector3f dest); + + /** + * Transform the given vector by the transpose of this matrix. + * + * @param v + * the vector to transform + * @return v + */ + Vector3f transformTranspose(Vector3f v); + + /** + * Transform the given vector by the transpose of this matrix and store the result in dest. + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformTranspose(Vector3fc v, Vector3f dest); + + /** + * Transform the vector (x, y, z) by the transpose of this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformTranspose(float x, float y, float z, Vector3f dest); + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateX(float ang, Matrix3f dest); + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateY(float ang, Matrix3f dest); + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateZ(float ang, Matrix3f dest); + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX, dest).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateXYZ(float angleX, float angleY, float angleZ, Matrix3f dest); + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ, dest).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateZYX(float angleZ, float angleY, float angleX, Matrix3f dest); + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY, dest).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateYXZ(float angleY, float angleX, float angleZ, Matrix3f dest); + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the given axis specified as x, y and z components, and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotate(float ang, float x, float y, float z, Matrix3f dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateLocal(float ang, float x, float y, float z, Matrix3f dest); + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateLocalX(float ang, Matrix3f dest); + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateLocalY(float ang, Matrix3f dest); + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateLocalZ(float ang, Matrix3f dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotate(Quaternionfc quat, Matrix3f dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateLocal(Quaternionfc quat, Matrix3f dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float, Matrix3f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotate(AxisAngle4f axisAngle, Matrix3f dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float, Matrix3f) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotate(float angle, Vector3fc axis, Matrix3f dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + * + * @see #lookAlong(float, float, float, float, float, float, Matrix3f) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix3f lookAlong(Vector3fc dir, Vector3fc up, Matrix3f dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix3f lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix3f dest); + + /** + * Get the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param dest + * will hold the row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + Vector3f getRow(int row, Vector3f dest) throws IndexOutOfBoundsException; + + /** + * Get the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..2] + * @param dest + * will hold the column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..2] + */ + Vector3f getColumn(int column, Vector3f dest) throws IndexOutOfBoundsException; + + /** + * Get the matrix element value at the given column and row. + * + * @param column + * the colum index in [0..2] + * @param row + * the row index in [0..2] + * @return the element value + */ + float get(int column, int row); + + /** + * Get the matrix element value at the given row and column. + * + * @param row + * the row index in [0..2] + * @param column + * the colum index in [0..2] + * @return the element value + */ + float getRowColumn(int row, int column); + + /** + * Compute a normal matrix from this matrix and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f normal(Matrix3f dest); + + /** + * Compute the cofactor matrix of this and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f cofactor(Matrix3f dest); + + /** + * Get the scaling factors of this matrix for the three base axes. + * + * @param dest + * will hold the scaling factors for x, y and z + * @return dest + */ + Vector3f getScale(Vector3f dest); + + /** + * Obtain the direction of +Z before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3f inv = new Matrix3f(this).invert();
+     * inv.transform(dir.set(0, 0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveZ(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3f positiveZ(Vector3f dir); + + /** + * Obtain the direction of +Z before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3f inv = new Matrix3f(this).transpose();
+     * inv.transform(dir.set(0, 0, 1));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3f normalizedPositiveZ(Vector3f dir); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3f inv = new Matrix3f(this).invert();
+     * inv.transform(dir.set(1, 0, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3f positiveX(Vector3f dir); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3f inv = new Matrix3f(this).transpose();
+     * inv.transform(dir.set(1, 0, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3f normalizedPositiveX(Vector3f dir); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3f inv = new Matrix3f(this).invert();
+     * inv.transform(dir.set(0, 1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3f positiveY(Vector3f dir); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3f inv = new Matrix3f(this).transpose();
+     * inv.transform(dir.set(0, 1, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3f normalizedPositiveY(Vector3f dir); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix3f add(Matrix3fc other, Matrix3f dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix3f sub(Matrix3fc subtrahend, Matrix3f dest); + + /** + * Component-wise multiply this by other and store the result in dest. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mulComponentWise(Matrix3fc other, Matrix3f dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Matrix3f lerp(Matrix3fc other, float t, Matrix3f dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mul(new Matrix3f().lookAlong(new Vector3f(dir).negate(), up).invert(), dest) + * + * @see #rotateTowards(float, float, float, float, float, float, Matrix3f) + * + * @param direction + * the direction to rotate towards + * @param up + * the model's up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateTowards(Vector3fc direction, Vector3fc up, Matrix3f dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mul(new Matrix3f().lookAlong(-dirX, -dirY, -dirZ, upX, upY, upZ).invert(), dest) + * + * @see #rotateTowards(Vector3fc, Vector3fc, Matrix3f) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix3f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix3f dest); + + /** + * Extract the Euler angles from the rotation represented by this matrix and store the extracted Euler angles in dest. + *

+ * This method assumes that this matrix only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + *

+ * Note that the returned Euler angles must be applied in the order X * Y * Z to obtain the identical matrix. + * This means that calling {@link Matrix3fc#rotateXYZ(float, float, float, Matrix3f)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix3f m = ...; // <- matrix only representing rotation
+     * Matrix3f n = new Matrix3f();
+     * n.rotateXYZ(m.getEulerAnglesXYZ(new Vector3f()));
+     * 
+ *

+ * Reference: http://en.wikipedia.org/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3f getEulerAnglesXYZ(Vector3f dest); + + /** + * Extract the Euler angles from the rotation represented by this matrix and store the extracted Euler angles in dest. + *

+ * This method assumes that this matrix only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + *

+ * Note that the returned Euler angles must be applied in the order Z * Y * X to obtain the identical matrix. + * This means that calling {@link Matrix3fc#rotateZYX(float, float, float, Matrix3f)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix3f m = ...; // <- matrix only representing rotation
+     * Matrix3f n = new Matrix3f();
+     * n.rotateZYX(m.getEulerAnglesZYX(new Vector3f()));
+     * 
+ *

+ * Reference: http://en.wikipedia.org/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3f getEulerAnglesZYX(Vector3f dest); + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a
+     * 0 1 b
+     * 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + Matrix3f obliqueZ(float a, float b, Matrix3f dest); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix3fc m, float delta); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through the given plane + * specified via the plane normal (nx, ny, nz), and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param dest + * will hold the result + * @return this + */ + Matrix3f reflect(float nx, float ny, float nz, Matrix3f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through a plane + * specified via the plane orientation, and store the result in dest. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation + * @param dest + * will hold the result + * @return this + */ + Matrix3f reflect(Quaternionfc orientation, Matrix3f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects through the given plane + * specified via the plane normal, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param dest + * will hold the result + * @return this + */ + Matrix3f reflect(Vector3fc normal, Matrix3f dest); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + * Compute (x, y, z)^T * this * (x, y, z). + * + * @param x + * the x coordinate of the vector to multiply + * @param y + * the y coordinate of the vector to multiply + * @param z + * the z coordinate of the vector to multiply + * @return the result + */ + float quadraticFormProduct(float x, float y, float z); + + /** + * Compute v^T * this * v. + * + * @param v + * the vector to multiply + * @return the result + */ + float quadraticFormProduct(Vector3fc v); + + /** + * Multiply this by the matrix + *

+     * 1 0 0
+     * 0 0 1
+     * 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapXZY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 1 0  0
+     * 0 0 -1
+     * 0 1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapXZnY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0
+     * 0 -1  0
+     * 0  0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapXnYnZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 1  0 0
+     * 0  0 1
+     * 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapXnZY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0
+     * 0  0 -1
+     * 0 -1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapXnZnY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0
+     * 1 0 0
+     * 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapYXZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0
+     * 1 0  0
+     * 0 0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapYXnZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1
+     * 1 0 0
+     * 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapYZX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1
+     * 1 0  0
+     * 0 1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapYZnX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0
+     * 1  0 0
+     * 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapYnXZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0
+     * 1  0  0
+     * 0  0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapYnXnZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1
+     * 1  0 0
+     * 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapYnZX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1
+     * 1  0  0
+     * 0 -1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapYnZnX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0
+     * 0 0 1
+     * 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapZXY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0
+     * 0 0 -1
+     * 1 0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapZXnY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1
+     * 0 1 0
+     * 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapZYX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1
+     * 0 1  0
+     * 1 0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapZYnX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0
+     * 0  0 1
+     * 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapZnXY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0
+     * 0  0 -1
+     * 1  0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapZnXnY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1
+     * 0 -1 0
+     * 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapZnYX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1
+     * 0 -1  0
+     * 1  0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapZnYnX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0
+     *  0 1  0
+     *  0 0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnXYnZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * -1 0 0
+     *  0 0 1
+     *  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnXZY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0
+     *  0 0 -1
+     *  0 1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnXZnY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0
+     *  0 -1 0
+     *  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnXnYZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0
+     *  0 -1  0
+     *  0  0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnXnYnZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0
+     *  0  0 1
+     *  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnXnZY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0
+     *  0  0 -1
+     *  0 -1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnXnZnY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0
+     * -1 0 0
+     *  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnYXZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0
+     * -1 0  0
+     *  0 0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnYXnZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1
+     * -1 0 0
+     *  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnYZX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1
+     * -1 0  0
+     *  0 1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnYZnX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0
+     * -1  0 0
+     *  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnYnXZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0
+     * -1  0  0
+     *  0  0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnYnXnZ(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1
+     * -1  0 0
+     *  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnYnZX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1
+     * -1  0  0
+     *  0 -1  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnYnZnX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0
+     *  0 0 1
+     * -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnZXY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0
+     *  0 0 -1
+     * -1 0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnZXnY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1
+     *  0 1 0
+     * -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnZYX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1
+     *  0 1  0
+     * -1 0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnZYnX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0
+     *  0  0 1
+     * -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnZnXY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0
+     *  0  0 -1
+     * -1  0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnZnXnY(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1
+     *  0 -1 0
+     * -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnZnYX(Matrix3f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1
+     *  0 -1  0
+     * -1  0  0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f mapnZnYnX(Matrix3f dest); + + /** + * Multiply this by the matrix + *
+     * -1 0 0
+     *  0 1 0
+     *  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f negateX(Matrix3f dest); + + /** + * Multiply this by the matrix + *
+     * 1  0 0
+     * 0 -1 0
+     * 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f negateY(Matrix3f dest); + + /** + * Multiply this by the matrix + *
+     * 1 0  0
+     * 0 1  0
+     * 0 0 -1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f negateZ(Matrix3f dest); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2d.java new file mode 100644 index 000000000..527084c25 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2d.java @@ -0,0 +1,2497 @@ +/* + * The MIT License + * + * Copyright (c) 2017-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + + +/** + * Contains the definition of a 3x2 matrix of doubles, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10 m20
+ * m01 m11 m21
+ * + * @author Kai Burjack + */ +public class Matrix3x2d implements Matrix3x2dc, Cloneable, Externalizable { + + private static final long serialVersionUID = 1L; + + public double m00, m01; + public double m10, m11; + public double m20, m21; + + /** + * Create a new {@link Matrix3x2d} and set it to {@link #identity() identity}. + */ + public Matrix3x2d() { + this.m00 = 1.0; + this.m11 = 1.0; + } + + /** + * Create a new {@link Matrix3x2d} by setting its left 2x2 submatrix to the values of the given {@link Matrix2dc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix2dc} + */ + public Matrix3x2d(Matrix2dc mat) { + if (mat instanceof Matrix2d) { + MemUtil.INSTANCE.copy((Matrix2d) mat, this); + } else { + setMatrix2dc(mat); + } + } + + /** + * Create a new {@link Matrix3x2d} by setting its left 2x2 submatrix to the values of the given {@link Matrix2fc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix2fc} + */ + public Matrix3x2d(Matrix2fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Create a new {@link Matrix3x2d} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix3x2dc} to copy the values from + */ + public Matrix3x2d(Matrix3x2dc mat) { + if (mat instanceof Matrix3x2d) { + MemUtil.INSTANCE.copy((Matrix3x2d) mat, this); + } else { + setMatrix3x2dc(mat); + } + } + + /** + * Create a new 3x2 matrix using the supplied double values. The order of the parameter is column-major, + * so the first two parameters specify the two elements of the first column. + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + * @param m20 + * the value of m20 + * @param m21 + * the value of m21 + */ + public Matrix3x2d(double m00, double m01, + double m10, double m11, + double m20, double m21) { + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + this.m20 = m20; + this.m21 = m21; + } + + /** + * Create a new {@link Matrix3x2d} by reading its 6 double components from the given {@link DoubleBuffer} + * at the buffer's current position. + *

+ * That DoubleBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link DoubleBuffer} to read the matrix values from + */ + public Matrix3x2d(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + public double m00() { + return m00; + } + public double m01() { + return m01; + } + public double m10() { + return m10; + } + public double m11() { + return m11; + } + public double m20() { + return m20; + } + public double m21() { + return m21; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + Matrix3x2d _m00(double m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + Matrix3x2d _m01(double m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + Matrix3x2d _m10(double m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + Matrix3x2d _m11(double m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + Matrix3x2d _m20(double m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + Matrix3x2d _m21(double m21) { + this.m21 = m21; + return this; + } + + /** + * Set the elements of this matrix to the ones in m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix3x2d set(Matrix3x2dc m) { + if (m instanceof Matrix3x2d) { + MemUtil.INSTANCE.copy((Matrix3x2d) m, this); + } else { + setMatrix3x2dc(m); + } + return this; + } + private void setMatrix3x2dc(Matrix3x2dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + m20 = mat.m20(); + m21 = mat.m21(); + } + + /** + * Set the left 2x2 submatrix of this {@link Matrix3x2d} to the given {@link Matrix2dc} and don't change the other elements. + * + * @param m + * the 2x2 matrix + * @return this + */ + public Matrix3x2d set(Matrix2dc m) { + if (m instanceof Matrix2d) { + MemUtil.INSTANCE.copy((Matrix2d) m, this); + } else { + setMatrix2dc(m); + } + return this; + } + private void setMatrix2dc(Matrix2dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Set the left 2x2 submatrix of this {@link Matrix3x2d} to the given {@link Matrix2fc} and don't change the other elements. + * + * @param m + * the 2x2 matrix + * @return this + */ + public Matrix3x2d set(Matrix2fc m) { + m00 = m.m00(); + m01 = m.m01(); + m10 = m.m10(); + m11 = m.m11(); + return this; + } + + /** + * Multiply this matrix by the supplied right matrix by assuming a third row in + * both matrices of (0, 0, 1). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix3x2d mul(Matrix3x2dc right) { + return mul(right, this); + } + + /** + * Multiply this matrix by the supplied right matrix by assuming a third row in + * both matrices of (0, 0, 1) and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d mul(Matrix3x2dc right, Matrix3x2d dest) { + double nm00 = m00 * right.m00() + m10 * right.m01(); + double nm01 = m01 * right.m00() + m11 * right.m01(); + double nm10 = m00 * right.m10() + m10 * right.m11(); + double nm11 = m01 * right.m10() + m11 * right.m11(); + double nm20 = m00 * right.m20() + m10 * right.m21() + m20; + double nm21 = m01 * right.m20() + m11 * right.m21() + m21; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = nm20; + dest.m21 = nm21; + return dest; + } + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in this. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @return this + */ + public Matrix3x2d mulLocal(Matrix3x2dc left) { + return mulLocal(left, this); + } + + public Matrix3x2d mulLocal(Matrix3x2dc left, Matrix3x2d dest) { + double nm00 = left.m00() * m00 + left.m10() * m01; + double nm01 = left.m01() * m00 + left.m11() * m01; + double nm10 = left.m00() * m10 + left.m10() * m11; + double nm11 = left.m01() * m10 + left.m11() * m11; + double nm20 = left.m00() * m20 + left.m10() * m21 + left.m20(); + double nm21 = left.m01() * m20 + left.m11() * m21 + left.m21(); + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = nm20; + dest.m21 = nm21; + return dest; + } + + /** + * Set the values within this matrix to the supplied double values. The result looks like this: + *

+ * m00, m10, m20
+ * m01, m11, m21
+ * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @param m20 + * the new value of m20 + * @param m21 + * the new value of m21 + * @return this + */ + public Matrix3x2d set(double m00, double m01, + double m10, double m11, + double m20, double m21) { + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + this.m20 = m20; + this.m21 = m21; + return this; + } + + /** + * Set the values in this matrix based on the supplied double array. The result looks like this: + *

+ * 0, 2, 4
+ * 1, 3, 5
+ * + * This method only uses the first 6 values, all others are ignored. + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix3x2d set(double m[]) { + MemUtil.INSTANCE.copy(m, 0, this); + return this; + } + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + public double determinant() { + return m00 * m11 - m01 * m10; + } + + /** + * Invert this matrix by assuming a third row in this matrix of (0, 0, 1). + * + * @return this + */ + public Matrix3x2d invert() { + return invert(this); + } + + /** + * Invert the this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d invert(Matrix3x2d dest) { + // client must make sure that matrix is invertible + double s = 1.0 / (m00 * m11 - m01 * m10); + double nm00 = m11 * s; + double nm01 = -m01 * s; + double nm10 = -m10 * s; + double nm11 = m00 * s; + double nm20 = (m10 * m21 - m20 * m11) * s; + double nm21 = (m20 * m01 - m00 * m21) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = nm20; + dest.m21 = nm21; + return dest; + } + + /** + * Set this matrix to be a simple translation matrix in a two-dimensional coordinate system. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + *

+ * In order to apply a translation via to an already existing transformation + * matrix, use {@link #translate(double, double) translate()} instead. + * + * @see #translate(double, double) + * + * @param x + * the units to translate in x + * @param y + * the units to translate in y + * @return this + */ + public Matrix3x2d translation(double x, double y) { + m00 = 1.0; + m01 = 0.0; + m10 = 0.0; + m11 = 1.0; + m20 = x; + m21 = y; + return this; + } + + /** + * Set this matrix to be a simple translation matrix in a two-dimensional coordinate system. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + *

+ * In order to apply a translation via to an already existing transformation + * matrix, use {@link #translate(Vector2dc) translate()} instead. + * + * @see #translate(Vector2dc) + * + * @param offset + * the translation + * @return this + */ + public Matrix3x2d translation(Vector2dc offset) { + return translation(offset.x(), offset.y()); + } + + /** + * Set only the translation components of this matrix (m20, m21) to the given values (x, y). + *

+ * To build a translation matrix instead, use {@link #translation(double, double)}. + * To apply a translation to another matrix, use {@link #translate(double, double)}. + * + * @see #translation(double, double) + * @see #translate(double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @return this + */ + public Matrix3x2d setTranslation(double x, double y) { + m20 = x; + m21 = y; + return this; + } + + /** + * Set only the translation components of this matrix (m20, m21) to the given values (offset.x, offset.y). + *

+ * To build a translation matrix instead, use {@link #translation(Vector2dc)}. + * To apply a translation to another matrix, use {@link #translate(Vector2dc)}. + * + * @see #translation(Vector2dc) + * @see #translate(Vector2dc) + * + * @param offset + * the new translation to set + * @return this + */ + public Matrix3x2d setTranslation(Vector2dc offset) { + return setTranslation(offset.x(), offset.y()); + } + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y and store the result + * in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(double, double)}. + * + * @see #translation(double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d translate(double x, double y, Matrix3x2d dest) { + double rm20 = x; + double rm21 = y; + dest.m20 = m00 * rm20 + m10 * rm21 + m20; + dest.m21 = m01 * rm20 + m11 * rm21 + m21; + dest.m00 = m00; + dest.m01 = m01; + dest.m10 = m10; + dest.m11 = m11; + return dest; + } + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(double, double)}. + * + * @see #translation(double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @return this + */ + public Matrix3x2d translate(double x, double y) { + return translate(x, y, this); + } + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y, and + * store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector2dc)}. + * + * @see #translation(Vector2dc) + * + * @param offset + * the offset to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d translate(Vector2dc offset, Matrix3x2d dest) { + return translate(offset.x(), offset.y(), dest); + } + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector2dc)}. + * + * @see #translation(Vector2dc) + * + * @param offset + * the offset to translate + * @return this + */ + public Matrix3x2d translate(Vector2dc offset) { + return translate(offset.x(), offset.y(), this); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector2dc)}. + * + * @see #translation(Vector2dc) + * + * @param offset + * the number of units in x and y by which to translate + * @return this + */ + public Matrix3x2d translateLocal(Vector2dc offset) { + return translateLocal(offset.x(), offset.y()); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector2dc)}. + * + * @see #translation(Vector2dc) + * + * @param offset + * the number of units in x and y by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d translateLocal(Vector2dc offset, Matrix3x2d dest) { + return translateLocal(offset.x(), offset.y(), dest); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(double, double)}. + * + * @see #translation(double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d translateLocal(double x, double y, Matrix3x2d dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m10 = m10; + dest.m11 = m11; + dest.m20 = m20 + x; + dest.m21 = m21 + y; + return dest; + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(double, double)}. + * + * @see #translation(double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @return this + */ + public Matrix3x2d translateLocal(double x, double y) { + return translateLocal(x, y, this); + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + " " + Runtime.format(m20, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + " " + Runtime.format(m21, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix3x2dc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix3x2dc) + * + * @param dest + * the destination matrix + * @return dest + */ + public Matrix3x2d get(Matrix3x2d dest) { + return dest.set(this); + } + + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public DoubleBuffer get(DoubleBuffer buffer) { + return get(buffer.position(), buffer); + } + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public DoubleBuffer get(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public ByteBuffer get(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get3x3(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x3(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public DoubleBuffer get3x3(DoubleBuffer buffer) { + MemUtil.INSTANCE.put3x3(this, 0, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public DoubleBuffer get3x3(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put3x3(this, index, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get3x3(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x3(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public ByteBuffer get3x3(ByteBuffer buffer) { + MemUtil.INSTANCE.put3x3(this, 0, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public ByteBuffer get3x3(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put3x3(this, index, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get4x4(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public DoubleBuffer get4x4(DoubleBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, 0, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public DoubleBuffer get4x4(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, index, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get4x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public ByteBuffer get4x4(ByteBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, 0, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public ByteBuffer get4x4(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, index, buffer); + return buffer; + } + + public Matrix3x2dc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Store this matrix into the supplied double array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + public double[] get(double[] arr, int offset) { + MemUtil.INSTANCE.copy(this, arr, offset); + return arr; + } + + /** + * Store this matrix into the supplied double array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(double[], int)}. + * + * @see #get(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + public double[] get(double[] arr) { + return get(arr, 0); + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied float array at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + public double[] get3x3(double[] arr, int offset) { + MemUtil.INSTANCE.copy3x3(this, arr, offset); + return arr; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied float array. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get3x3(double[], int)}. + * + * @see #get3x3(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + public double[] get3x3(double[] arr) { + return get3x3(arr, 0); + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied float array at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + public double[] get4x4(double[] arr, int offset) { + MemUtil.INSTANCE.copy4x4(this, arr, offset); + return arr; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied float array. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get4x4(double[], int)}. + * + * @see #get4x4(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + public double[] get4x4(double[] arr) { + return get4x4(arr, 0); + } + + /** + * Set the values of this matrix by reading 6 double values from the given {@link DoubleBuffer} in column-major order, + * starting at its current position. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2d set(DoubleBuffer buffer) { + int pos = buffer.position(); + MemUtil.INSTANCE.get(this, pos, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 6 double values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2d set(ByteBuffer buffer) { + int pos = buffer.position(); + MemUtil.INSTANCE.get(this, pos, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 6 double values from the given {@link DoubleBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2d set(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 6 double values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2d set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + /** + * Set the values of this matrix by reading 6 double values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2d setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set all values within this matrix to zero. + * + * @return this + */ + public Matrix3x2d zero() { + MemUtil.INSTANCE.zero(this); + return this; + } + + /** + * Set this matrix to the identity. + * + * @return this + */ + public Matrix3x2d identity() { + MemUtil.INSTANCE.identity(this); + return this; + } + + /** + * Apply scaling to this matrix by scaling the unit axes by the given x and y and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d scale(double x, double y, Matrix3x2d dest) { + dest.m00 = m00 * x; + dest.m01 = m01 * x; + dest.m10 = m10 * y; + dest.m11 = m11 * y; + dest.m20 = m20; + dest.m21 = m21; + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x and y factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix3x2d scale(double x, double y) { + return scale(x, y, this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @return this + */ + public Matrix3x2d scale(Vector2dc xy) { + return scale(xy.x(), xy.y(), this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d scale(Vector2dc xy, Matrix3x2d dest) { + return scale(xy.x(), xy.y(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @return this + */ + public Matrix3x2d scale(Vector2fc xy) { + return scale(xy.x(), xy.y(), this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d scale(Vector2fc xy, Matrix3x2d dest) { + return scale(xy.x(), xy.y(), dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling the two base axes by the given xy factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @see #scale(double, double, Matrix3x2d) + * + * @param xy + * the factor for the two components + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d scale(double xy, Matrix3x2d dest) { + return scale(xy, xy, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling the two base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @see #scale(double, double) + * + * @param xy + * the factor for the two components + * @return this + */ + public Matrix3x2d scale(double xy) { + return scale(xy, xy); + } + + public Matrix3x2d scaleLocal(double x, double y, Matrix3x2d dest) { + dest.m00 = x * m00; + dest.m01 = y * m01; + dest.m10 = x * m10; + dest.m11 = y * m11; + dest.m20 = x * m20; + dest.m21 = y * m21; + return dest; + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x and y factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix3x2d scaleLocal(double x, double y) { + return scaleLocal(x, y, this); + } + + public Matrix3x2d scaleLocal(double xy, Matrix3x2d dest) { + return scaleLocal(xy, xy, dest); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given xy factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param xy + * the factor of the x and y component + * @return this + */ + public Matrix3x2d scaleLocal(double xy) { + return scaleLocal(xy, xy, this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using (ox, oy) as the scaling origin, and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, dest).scale(sx, sy).translate(-ox, -oy) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d scaleAround(double sx, double sy, double ox, double oy, Matrix3x2d dest) { + double nm20 = m00 * ox + m10 * oy + m20; + double nm21 = m01 * ox + m11 * oy + m21; + dest.m00 = m00 * sx; + dest.m01 = m01 * sx; + dest.m10 = m10 * sy; + dest.m11 = m11 * sy; + dest.m20 = dest.m00 * -ox + dest.m10 * -oy + nm20; + dest.m21 = dest.m01 * -ox + dest.m11 * -oy + nm21; + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using (ox, oy) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy).scale(sx, sy).translate(-ox, -oy) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @return this + */ + public Matrix3x2d scaleAround(double sx, double sy, double ox, double oy) { + return scaleAround(sx, sy, ox, oy, this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, dest).scale(factor).translate(-ox, -oy) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + public Matrix3x2d scaleAround(double factor, double ox, double oy, Matrix3x2d dest) { + return scaleAround(factor, factor, ox, oy, this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy).scale(factor).translate(-ox, -oy) + * + * @param factor + * the scaling factor for all axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @return this + */ + public Matrix3x2d scaleAround(double factor, double ox, double oy) { + return scaleAround(factor, factor, ox, oy, this); + } + + public Matrix3x2d scaleAroundLocal(double sx, double sy, double ox, double oy, Matrix3x2d dest) { + dest.m00 = sx * m00; + dest.m01 = sy * m01; + dest.m10 = sx * m10; + dest.m11 = sy * m11; + dest.m20 = sx * m20 - sx * ox + ox; + dest.m21 = sy * m21 - sy * oy + oy; + return dest; + } + + public Matrix3x2d scaleAroundLocal(double factor, double ox, double oy, Matrix3x2d dest) { + return scaleAroundLocal(factor, factor, ox, oy, dest); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using (ox, oy) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix3x2d().translate(ox, oy).scale(sx, sy).translate(-ox, -oy).mul(this, this) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix3x2d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz) { + return scaleAroundLocal(sx, sy, ox, oy, this); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix3x2d().translate(ox, oy).scale(factor).translate(-ox, -oy).mul(this, this) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @return this + */ + public Matrix3x2d scaleAroundLocal(double factor, double ox, double oy) { + return scaleAroundLocal(factor, factor, ox, oy, this); + } + + /** + * Set this matrix to be a simple scale matrix, which scales the two base axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a matrix, use {@link #scale(double) scale()} instead. + * + * @see #scale(double) + * + * @param factor + * the scale factor in x and y + * @return this + */ + public Matrix3x2d scaling(double factor) { + return scaling(factor, factor); + } + + /** + * Set this matrix to be a simple scale matrix. + * + * @param x + * the scale in x + * @param y + * the scale in y + * @return this + */ + public Matrix3x2d scaling(double x, double y) { + m00 = x; + m01 = 0.0; + m10 = 0.0; + m11 = y; + m20 = 0.0; + m21 = 0.0; + return this; + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(double) rotate()} instead. + * + * @see #rotate(double) + * + * @param angle + * the angle in radians + * @return this + */ + public Matrix3x2d rotation(double angle) { + double cos = Math.cos(angle); + double sin = Math.sin(angle); + m00 = cos; + m10 = -sin; + m20 = 0.0; + m01 = sin; + m11 = cos; + m21 = 0.0; + return this; + } + + /** + * Transform/multiply the given vector by this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in that vector. + * + * @see Vector3d#mul(Matrix3x2dc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + public Vector3d transform(Vector3d v) { + return v.mul(this); + } + + /** + * Transform/multiply the given vector by this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in dest. + * + * @see Vector3d#mul(Matrix3x2dc, Vector3d) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + public Vector3d transform(Vector3dc v, Vector3d dest) { + return v.mul(this, dest); + } + + /** + * Transform/multiply the given vector (x, y, z) by this matrix and store the result in dest. + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param z + * the z component of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + public Vector3d transform(double x, double y, double z, Vector3d dest) { + return dest.set(m00 * x + m10 * y + m20 * z, m01 * x + m11 * y + m21 * z, z); + } + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=1, by + * this matrix and store the result in that vector. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in another vector, use {@link #transformPosition(Vector2dc, Vector2d)}. + * + * @see #transformPosition(Vector2dc, Vector2d) + * @see #transform(Vector3d) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + public Vector2d transformPosition(Vector2d v) { + v.set(m00 * v.x + m10 * v.y + m20, + m01 * v.x + m11 * v.y + m21); + return v; + } + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=1, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector2d)}. + * + * @see #transformPosition(Vector2d) + * @see #transform(Vector3dc, Vector3d) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector2d transformPosition(Vector2dc v, Vector2d dest) { + dest.set(m00 * v.x() + m10 * v.y() + m20, + m01 * v.x() + m11 * v.y() + m21); + return dest; + } + + /** + * Transform/multiply the given 2D-vector (x, y), as if it was a 3D-vector with z=1, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector2d)}. + * + * @see #transformPosition(Vector2d) + * @see #transform(Vector3dc, Vector3d) + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector2d transformPosition(double x, double y, Vector2d dest) { + return dest.set(m00 * x + m10 * y + m20, m01 * x + m11 * y + m21); + } + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=0, by + * this matrix and store the result in that vector. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector2dc, Vector2d)}. + * + * @see #transformDirection(Vector2dc, Vector2d) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + public Vector2d transformDirection(Vector2d v) { + v.set(m00 * v.x + m10 * v.y, + m01 * v.x + m11 * v.y); + return v; + } + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=0, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector2d)}. + * + * @see #transformDirection(Vector2d) + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + public Vector2d transformDirection(Vector2dc v, Vector2d dest) { + dest.set(m00 * v.x() + m10 * v.y(), + m01 * v.x() + m11 * v.y()); + return dest; + } + + /** + * Transform/multiply the given 2D-vector (x, y), as if it was a 3D-vector with z=0, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector2d)}. + * + * @see #transformDirection(Vector2d) + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector2d transformDirection(double x, double y, Vector2d dest) { + return dest.set(m00 * x + m10 * y, m01 * x + m11 * y); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(m00); + out.writeDouble(m01); + out.writeDouble(m10); + out.writeDouble(m11); + out.writeDouble(m20); + out.writeDouble(m21); + } + + public void readExternal(ObjectInput in) throws IOException { + m00 = in.readDouble(); + m01 = in.readDouble(); + m10 = in.readDouble(); + m11 = in.readDouble(); + m20 = in.readDouble(); + m21 = in.readDouble(); + } + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3x2d rotate(double ang) { + return rotate(ang, this); + } + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d rotate(double ang, Matrix3x2d dest) { + double cos = Math.cos(ang); + double sin = Math.sin(ang); + double rm00 = cos; + double rm01 = sin; + double rm10 = -sin; + double rm11 = cos; + double nm00 = m00 * rm00 + m10 * rm01; + double nm01 = m01 * rm00 + m11 * rm01; + dest.m10 = m00 * rm10 + m10 * rm11; + dest.m11 = m01 * rm10 + m11 * rm11; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m20 = m20; + dest.m21 = m21; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double) + * + * @param ang + * the angle in radians to rotate + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d rotateLocal(double ang, Matrix3x2d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm00 = cos * m00 - sin * m01; + double nm01 = sin * m00 + cos * m01; + double nm10 = cos * m10 - sin * m11; + double nm11 = sin * m10 + cos * m11; + double nm20 = cos * m20 - sin * m21; + double nm21 = sin * m20 + cos * m21; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = nm20; + dest.m21 = nm21; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double) + * + * @param ang + * the angle in radians to rotate + * @return this + */ + public Matrix3x2d rotateLocal(double ang) { + return rotateLocal(ang, this); + } + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians about + * the specified rotation center (x, y). + *

+ * This method is equivalent to calling: translate(x, y).rotate(ang).translate(-x, -y) + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @see #translate(double, double) + * @see #rotate(double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the rotation center + * @param y + * the y component of the rotation center + * @return this + */ + public Matrix3x2d rotateAbout(double ang, double x, double y) { + return rotateAbout(ang, x, y, this); + } + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians about + * the specified rotation center (x, y) and store the result in dest. + *

+ * This method is equivalent to calling: translate(x, y, dest).rotate(ang).translate(-x, -y) + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @see #translate(double, double, Matrix3x2d) + * @see #rotate(double, Matrix3x2d) + * + * @param ang + * the angle in radians + * @param x + * the x component of the rotation center + * @param y + * the y component of the rotation center + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d rotateAbout(double ang, double x, double y, Matrix3x2d dest) { + double tm20 = m00 * x + m10 * y + m20; + double tm21 = m01 * x + m11 * y + m21; + double cos = Math.cos(ang); + double sin = Math.sin(ang); + double nm00 = m00 * cos + m10 * sin; + double nm01 = m01 * cos + m11 * sin; + dest.m10 = m00 * -sin + m10 * cos; + dest.m11 = m01 * -sin + m11 * cos; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m20 = dest.m00 * -x + dest.m10 * -y + tm20; + dest.m21 = dest.m01 * -x + dest.m11 * -y + tm21; + return dest; + } + + /** + * Apply a rotation transformation to this matrix that rotates the given normalized fromDir direction vector + * to point along the normalized toDir, and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param fromDir + * the normalized direction which should be rotate to point along toDir + * @param toDir + * the normalized destination direction + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d rotateTo(Vector2dc fromDir, Vector2dc toDir, Matrix3x2d dest) { + double dot = fromDir.x() * toDir.x() + fromDir.y() * toDir.y(); + double det = fromDir.x() * toDir.y() - fromDir.y() * toDir.x(); + double rm00 = dot; + double rm01 = det; + double rm10 = -det; + double rm11 = dot; + double nm00 = m00 * rm00 + m10 * rm01; + double nm01 = m01 * rm00 + m11 * rm01; + dest.m10 = m00 * rm10 + m10 * rm11; + dest.m11 = m01 * rm10 + m11 * rm11; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m20 = m20; + dest.m21 = m21; + return dest; + } + + /** + * Apply a rotation transformation to this matrix that rotates the given normalized fromDir direction vector + * to point along the normalized toDir. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param fromDir + * the normalized direction which should be rotate to point along toDir + * @param toDir + * the normalized destination direction + * @return this + */ + public Matrix3x2d rotateTo(Vector2dc fromDir, Vector2dc toDir) { + return rotateTo(fromDir, toDir, this); + } + + /** + * Apply a "view" transformation to this matrix that maps the given (left, bottom) and + * (right, top) corners to (-1, -1) and (1, 1) respectively and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + * + * @see #setView(double, double, double, double) + * + * @param left + * the distance from the center to the left view edge + * @param right + * the distance from the center to the right view edge + * @param bottom + * the distance from the center to the bottom view edge + * @param top + * the distance from the center to the top view edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2d view(double left, double right, double bottom, double top, Matrix3x2d dest) { + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm20 = (left + right) / (left - right); + double rm21 = (bottom + top) / (bottom - top); + dest.m20 = m00 * rm20 + m10 * rm21 + m20; + dest.m21 = m01 * rm20 + m11 * rm21 + m21; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + return dest; + } + + /** + * Apply a "view" transformation to this matrix that maps the given (left, bottom) and + * (right, top) corners to (-1, -1) and (1, 1) respectively. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + * + * @see #setView(double, double, double, double) + * + * @param left + * the distance from the center to the left view edge + * @param right + * the distance from the center to the right view edge + * @param bottom + * the distance from the center to the bottom view edge + * @param top + * the distance from the center to the top view edge + * @return this + */ + public Matrix3x2d view(double left, double right, double bottom, double top) { + return view(left, right, bottom, top, this); + } + + /** + * Set this matrix to define a "view" transformation that maps the given (left, bottom) and + * (right, top) corners to (-1, -1) and (1, 1) respectively. + * + * @see #view(double, double, double, double) + * + * @param left + * the distance from the center to the left view edge + * @param right + * the distance from the center to the right view edge + * @param bottom + * the distance from the center to the bottom view edge + * @param top + * the distance from the center to the top view edge + * @return this + */ + public Matrix3x2d setView(double left, double right, double bottom, double top) { + m00 = 2.0 / (right - left); + m01 = 0.0; + m10 = 0.0; + m11 = 2.0 / (top - bottom); + m20 = (left + right) / (left - right); + m21 = (bottom + top) / (bottom - top); + return this; + } + + /** + * Obtain the position that gets transformed to the origin by this matrix. + * This can be used to get the position of the "camera" from a given view transformation matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2d inv = new Matrix3x2d(this).invert();
+     * inv.transform(origin.set(0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + public Vector2d origin(Vector2d origin) { + double s = 1.0 / (m00 * m11 - m01 * m10); + origin.x = (m10 * m21 - m20 * m11) * s; + origin.y = (m20 * m01 - m00 * m21) * s; + return origin; + } + + /** + * Obtain the extents of the view transformation of this matrix and store it in area. + * This can be used to determine which region of the screen (i.e. the NDC space) is covered by the view. + * + * @param area + * will hold the view area as [minX, minY, maxX, maxY] + * @return area + */ + public double[] viewArea(double[] area) { + double s = 1.0 / (m00 * m11 - m01 * m10); + double rm00 = m11 * s; + double rm01 = -m01 * s; + double rm10 = -m10 * s; + double rm11 = m00 * s; + double rm20 = (m10 * m21 - m20 * m11) * s; + double rm21 = (m20 * m01 - m00 * m21) * s; + double nxnyX = -rm00 - rm10; + double nxnyY = -rm01 - rm11; + double pxnyX = rm00 - rm10; + double pxnyY = rm01 - rm11; + double nxpyX = -rm00 + rm10; + double nxpyY = -rm01 + rm11; + double pxpyX = rm00 + rm10; + double pxpyY = rm01 + rm11; + double minX = nxnyX; + minX = minX < nxpyX ? minX : nxpyX; + minX = minX < pxnyX ? minX : pxnyX; + minX = minX < pxpyX ? minX : pxpyX; + double minY = nxnyY; + minY = minY < nxpyY ? minY : nxpyY; + minY = minY < pxnyY ? minY : pxnyY; + minY = minY < pxpyY ? minY : pxpyY; + double maxX = nxnyX; + maxX = maxX > nxpyX ? maxX : nxpyX; + maxX = maxX > pxnyX ? maxX : pxnyX; + maxX = maxX > pxpyX ? maxX : pxpyX; + double maxY = nxnyY; + maxY = maxY > nxpyY ? maxY : nxpyY; + maxY = maxY > pxnyY ? maxY : pxnyY; + maxY = maxY > pxpyY ? maxY : pxpyY; + area[0] = minX + rm20; + area[1] = minY + rm21; + area[2] = maxX + rm20; + area[3] = maxY + rm21; + return area; + } + + public Vector2d positiveX(Vector2d dir) { + double s = m00 * m11 - m01 * m10; + s = 1.0 / s; + dir.x = m11 * s; + dir.y = -m01 * s; + return dir.normalize(dir); + } + + public Vector2d normalizedPositiveX(Vector2d dir) { + dir.x = m11; + dir.y = -m01; + return dir; + } + + public Vector2d positiveY(Vector2d dir) { + double s = m00 * m11 - m01 * m10; + s = 1.0 / s; + dir.x = -m10 * s; + dir.y = m00 * s; + return dir.normalize(dir); + } + + public Vector2d normalizedPositiveY(Vector2d dir) { + dir.x = -m10; + dir.y = m00; + return dir; + } + + /** + * Unproject the given window coordinates (winX, winY) by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix3x2d)} and then the method {@link #unprojectInv(double, double, int[], Vector2d) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(double, double, int[], Vector2d) + * @see #invert(Matrix3x2d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + public Vector2d unproject(double winX, double winY, int[] viewport, Vector2d dest) { + double s = 1.0 / (m00 * m11 - m01 * m10); + double im00 = m11 * s; + double im01 = -m01 * s; + double im10 = -m10 * s; + double im11 = m00 * s; + double im20 = (m10 * m21 - m20 * m11) * s; + double im21 = (m20 * m01 - m00 * m21) * s; + double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0; + double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0; + dest.x = im00 * ndcX + im10 * ndcY + im20; + dest.y = im01 * ndcX + im11 * ndcY + im21; + return dest; + } + + /** + * Unproject the given window coordinates (winX, winY) by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(double, double, int[], Vector2d) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + * + * @see #unproject(double, double, int[], Vector2d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + public Vector2d unprojectInv(double winX, double winY, int[] viewport, Vector2d dest) { + double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0; + double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0; + dest.x = m00 * ndcX + m10 * ndcY + m20; + dest.y = m01 * ndcX + m11 * ndcY + m21; + return dest; + } + + /** + * Compute the extents of the coordinate system before this transformation was applied and store the resulting + * corner coordinates in corner and the span vectors in xDir and yDir. + *

+ * That means, given the maximum extents of the coordinate system between [-1..+1] in all dimensions, + * this method returns one corner and the length and direction of the two base axis vectors in the coordinate + * system before this transformation is applied, which transforms into the corner coordinates [-1, +1]. + * + * @param corner + * will hold one corner of the span + * @param xDir + * will hold the direction and length of the span along the positive X axis + * @param yDir + * will hold the direction and length of the span along the positive Y axis + * @return this + */ + public Matrix3x2d span(Vector2d corner, Vector2d xDir, Vector2d yDir) { + double s = 1.0 / (m00 * m11 - m01 * m10); + double nm00 = m11 * s, nm01 = -m01 * s, nm10 = -m10 * s, nm11 = m00 * s; + corner.x = -nm00 - nm10 + (m10 * m21 - m20 * m11) * s; + corner.y = -nm01 - nm11 + (m20 * m01 - m00 * m21) * s; + xDir.x = 2.0 * nm00; xDir.y = 2.0 * nm01; + yDir.x = 2.0 * nm10; yDir.y = 2.0 * nm11; + return this; + } + + public boolean testPoint(double x, double y) { + double nxX = +m00, nxY = +m10, nxW = 1.0f + m20; + double pxX = -m00, pxY = -m10, pxW = 1.0f - m20; + double nyX = +m01, nyY = +m11, nyW = 1.0f + m21; + double pyX = -m01, pyY = -m11, pyW = 1.0f - m21; + return nxX * x + nxY * y + nxW >= 0 && pxX * x + pxY * y + pxW >= 0 && + nyX * x + nyY * y + nyW >= 0 && pyX * x + pyY * y + pyW >= 0; + } + + public boolean testCircle(double x, double y, double r) { + double invl; + double nxX = +m00, nxY = +m10, nxW = 1.0f + m20; + invl = Math.invsqrt(nxX * nxX + nxY * nxY); + nxX *= invl; nxY *= invl; nxW *= invl; + double pxX = -m00, pxY = -m10, pxW = 1.0f - m20; + invl = Math.invsqrt(pxX * pxX + pxY * pxY); + pxX *= invl; pxY *= invl; pxW *= invl; + double nyX = +m01, nyY = +m11, nyW = 1.0f + m21; + invl = Math.invsqrt(nyX * nyX + nyY * nyY); + nyX *= invl; nyY *= invl; nyW *= invl; + double pyX = -m01, pyY = -m11, pyW = 1.0f - m21; + invl = Math.invsqrt(pyX * pyX + pyY * pyY); + pyX *= invl; pyY *= invl; pyW *= invl; + return nxX * x + nxY * y + nxW >= -r && pxX * x + pxY * y + pxW >= -r && + nyX * x + nyY * y + nyW >= -r && pyX * x + pyY * y + pyW >= -r; + } + + public boolean testAar(double minX, double minY, double maxX, double maxY) { + double nxX = +m00, nxY = +m10, nxW = 1.0f + m20; + double pxX = -m00, pxY = -m10, pxW = 1.0f - m20; + double nyX = +m01, nyY = +m11, nyW = 1.0f + m21; + double pyX = -m01, pyY = -m11, pyW = 1.0f - m21; + /* + * This is an implementation of the "2.4 Basic intersection test" of the mentioned site. + * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted. + */ + return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) >= -nxW && + pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) >= -pxW && + nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) >= -nyW && + pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) >= -pyW; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(m00); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m01); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m10); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m11); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m20); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m21); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Matrix3x2d other = (Matrix3x2d) obj; + if (Double.doubleToLongBits(m00) != Double.doubleToLongBits(other.m00)) + return false; + if (Double.doubleToLongBits(m01) != Double.doubleToLongBits(other.m01)) + return false; + if (Double.doubleToLongBits(m10) != Double.doubleToLongBits(other.m10)) + return false; + if (Double.doubleToLongBits(m11) != Double.doubleToLongBits(other.m11)) + return false; + if (Double.doubleToLongBits(m20) != Double.doubleToLongBits(other.m20)) + return false; + if (Double.doubleToLongBits(m21) != Double.doubleToLongBits(other.m21)) + return false; + return true; + } + + public boolean equals(Matrix3x2dc m, double delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix3x2d)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + if (!Runtime.equals(m20, m.m20(), delta)) + return false; + if (!Runtime.equals(m21, m.m21(), delta)) + return false; + return true; + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && + Math.isFinite(m10) && Math.isFinite(m11) && + Math.isFinite(m20) && Math.isFinite(m21); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2dStack.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2dStack.java new file mode 100644 index 000000000..1f6aa5d96 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2dStack.java @@ -0,0 +1,186 @@ +/* + * The MIT License + * + * Copyright (c) 2018-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A stack of many {@link Matrix3x2d} instances. This resembles the matrix stack known from legacy OpenGL. + *

+ * This {@link Matrix3x2dStack} class inherits from {@link Matrix3x2d}, so the current/top matrix is always the + * {@link Matrix3x2dStack}/{@link Matrix3x2d} itself. This affects all operations in {@link Matrix3x2d} that take + * another {@link Matrix3x2d} as parameter. If a {@link Matrix3x2dStack} is used as argument to those methods, the + * effective argument will always be the current matrix of the matrix stack. + * + * @author Kai Burjack + */ +public class Matrix3x2dStack extends Matrix3x2d implements Cloneable { + + private static final long serialVersionUID = 1L; + + /** + * The matrix stack as a non-growable array. The size of the stack must be specified in the {@link #Matrix3x2dStack(int) constructor}. + */ + private Matrix3x2d[] mats; + + /** + * The index of the "current" matrix within {@link #mats}. + */ + private int curr; + + /** + * Create a new {@link Matrix3x2dStack} of the given size. + *

+ * Initially the stack pointer is at zero and the current matrix is set to identity. + * + * @param stackSize + * the size of the stack. This must be at least 1, in which case the {@link Matrix3x2dStack} simply only consists of this + * {@link Matrix3x2d} + */ + public Matrix3x2dStack(int stackSize) { + if (stackSize < 1) { + throw new IllegalArgumentException("stackSize must be >= 1"); //$NON-NLS-1$ + } + mats = new Matrix3x2d[stackSize - 1]; + // Allocate all matrices up front to keep the promise of being "allocation-free" + for (int i = 0; i < mats.length; i++) { + mats[i] = new Matrix3x2d(); + } + } + + /** + * Do not invoke manually! Only meant for serialization. + *

+ * Invoking this constructor from client code will result in an inconsistent state of the + * created {@link Matrix3x2dStack} instance. + */ + public Matrix3x2dStack() { + /* Empty! */ + } + + /** + * Set the stack pointer to zero and set the current/bottom matrix to {@link #identity() identity}. + * + * @return this + */ + public Matrix3x2dStack clear() { + curr = 0; + identity(); + return this; + } + + /** + * Increment the stack pointer by one and set the values of the new current matrix to the one directly below it. + * + * @return this + */ + public Matrix3x2dStack pushMatrix() { + if (curr == mats.length) { + throw new IllegalStateException("max stack size of " + (curr + 1) + " reached"); //$NON-NLS-1$ //$NON-NLS-2$ + } + mats[curr++].set(this); + return this; + } + + /** + * Decrement the stack pointer by one. + *

+ * This will effectively dispose of the current matrix. + * + * @return this + */ + public Matrix3x2dStack popMatrix() { + if (curr == 0) { + throw new IllegalStateException("already at the bottom of the stack"); //$NON-NLS-1$ + } + set(mats[--curr]); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + curr; + for (int i = 0; i < curr; i++) { + result = prime * result + mats[i].hashCode(); + } + return result; + } + + /* + * Contract between Matrix3x2d and Matrix3x2dStack: + * + * - Matrix3x2d.equals(Matrix3x2dStack) is true iff all the 6 matrix elements are equal + * - Matrix3x2dStack.equals(Matrix3x2d) is true iff all the 6 matrix elements are equal + * - Matrix3x2dStack.equals(Matrix3x2dStack) is true iff all 6 matrix elements are equal AND the matrix arrays as well as the stack pointer are equal + * - everything else is inequal + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (obj instanceof Matrix3x2dStack) { + Matrix3x2dStack other = (Matrix3x2dStack) obj; + if (curr != other.curr) + return false; + for (int i = 0; i < curr; i++) { + if (!mats[i].equals(other.mats[i])) + return false; + } + } + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(curr); + for (int i = 0; i < curr; i++) { + out.writeObject(mats[i]); + } + } + + public void readExternal(ObjectInput in) throws IOException { + super.readExternal(in); + curr = in.readInt(); + mats = new Matrix3x2dStack[curr]; + for (int i = 0; i < curr; i++) { + Matrix3x2d m = new Matrix3x2d(); + m.readExternal(in); + mats[i] = m; + } + } + + public Object clone() throws CloneNotSupportedException { + Matrix3x2dStack cloned = (Matrix3x2dStack) super.clone(); + Matrix3x2d[] clonedMats = new Matrix3x2d[mats.length]; + for (int i = 0; i < mats.length; i++) + clonedMats[i] = (Matrix3x2d) mats[i].clone(); + cloned.mats = clonedMats; + return cloned; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2dc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2dc.java new file mode 100644 index 000000000..fa22f98a3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2dc.java @@ -0,0 +1,1196 @@ +/* + * The MIT License + * + * Copyright (c) 2017-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.util.*; + + +/** + * Interface to a read-only view of a 3x2 matrix of double-precision floats. + * + * @author Kai Burjack + */ +public interface Matrix3x2dc { + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + double m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + double m01(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + double m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + double m11(); + + /** + * Return the value of the matrix element at column 2 and row 0. + * + * @return the value of the matrix element + */ + double m20(); + + /** + * Return the value of the matrix element at column 2 and row 1. + * + * @return the value of the matrix element + */ + double m21(); + + /** + * Multiply this matrix by the supplied right matrix by assuming a third row in + * both matrices of (0, 0, 1) and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d mul(Matrix3x2dc right, Matrix3x2d dest); + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in dest. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix3x2d mulLocal(Matrix3x2dc left, Matrix3x2d dest); + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + double determinant(); + + /** + * Invert the this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d invert(Matrix3x2d dest); + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y and store the result + * in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d translate(double x, double y, Matrix3x2d dest); + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y, and + * store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param offset + * the offset to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d translate(Vector2dc offset, Matrix3x2d dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param offset + * the number of units in x and y by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d translateLocal(Vector2dc offset, Matrix3x2d dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d translateLocal(double x, double y, Matrix3x2d dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return dest + */ + Matrix3x2d get(Matrix3x2d dest); + + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get(DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer get(int index, DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get3x3(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x3(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get3x3(DoubleBuffer buffer); + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer get3x3(int index, DoubleBuffer buffer); + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get3x3(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x3(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get3x3(ByteBuffer buffer); + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get3x3(int index, ByteBuffer buffer); + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get4x4(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get4x4(DoubleBuffer buffer); + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer get4x4(int index, DoubleBuffer buffer); + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get4x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get4x4(ByteBuffer buffer); + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get4x4(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix3x2dc getToAddress(long address); + + /** + * Store this matrix into the supplied double array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] get(double[] arr, int offset); + + /** + * Store this matrix into the supplied double array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(double[], int)}. + * + * @see #get(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] get(double[] arr); + + /** + * Store this matrix as an equivalent 3x3 matrix into the supplied double array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] get3x3(double[] arr, int offset); + + /** + * Store this matrix as an equivalent 3x3 matrix into the supplied double array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get3x3(double[], int)}. + * + * @see #get3x3(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] get3x3(double[] arr); + + /** + * Store this matrix as an equivalent 4x4 matrix into the supplied double array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] get4x4(double[] arr, int offset); + + /** + * Store this matrix as an equivalent 4x4 matrix into the supplied double array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get4x4(double[], int)}. + * + * @see #get4x4(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] get4x4(double[] arr); + + /** + * Apply scaling to this matrix by scaling the unit axes by the given x and y and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d scale(double x, double y, Matrix3x2d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d scale(Vector2dc xy, Matrix3x2d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d scale(Vector2fc xy, Matrix3x2d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the two base axes by the given xy factor, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param xy + * the factor to scale all two base axes by + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d scaleLocal(double xy, Matrix3x2d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x and y + * factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d scaleLocal(double x, double y, Matrix3x2d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using the given (ox, oy) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix3x2d().translate(ox, oy).scale(sx, sy).translate(-ox, -oy).mul(this, dest) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d scaleAroundLocal(double sx, double sy, double ox, double oy, Matrix3x2d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix3x2d().translate(ox, oy).scale(factor).translate(-ox, -oy).mul(this, dest) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix3x2d scaleAroundLocal(double factor, double ox, double oy, Matrix3x2d dest); + + /** + * Apply scaling to this matrix by uniformly scaling the two base axes by the given xy factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @see #scale(double, double, Matrix3x2d) + * + * @param xy + * the factor for the two components + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d scale(double xy, Matrix3x2d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using (ox, oy) as the scaling origin, and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, dest).scale(sx, sy).translate(-ox, -oy) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d scaleAround(double sx, double sy, double ox, double oy, Matrix3x2d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, dest).scale(factor).translate(-ox, -oy) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix3x2d scaleAround(double factor, double ox, double oy, Matrix3x2d dest); + + /** + * Transform/multiply the given vector by this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in that vector. + * + * @see Vector3d#mul(Matrix3x2dc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3d transform(Vector3d v); + + /** + * Transform/multiply the given vector by this matrix and store the result in dest. + * + * @see Vector3d#mul(Matrix3x2dc, Vector3d) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3d transform(Vector3dc v, Vector3d dest); + + /** + * Transform/multiply the given vector (x, y, z) by this matrix and store the result in dest. + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param z + * the z component of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3d transform(double x, double y, double z, Vector3d dest); + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=1, by + * this matrix and store the result in that vector. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in another vector, use {@link #transformPosition(Vector2dc, Vector2d)}. + * + * @see #transformPosition(Vector2dc, Vector2d) + * @see #transform(Vector3d) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector2d transformPosition(Vector2d v); + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=1, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector2d)}. + * + * @see #transformPosition(Vector2d) + * @see #transform(Vector3dc, Vector3d) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2d transformPosition(Vector2dc v, Vector2d dest); + + /** + * Transform/multiply the given 2D-vector (x, y), as if it was a 3D-vector with z=1, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector2d)}. + * + * @see #transformPosition(Vector2d) + * @see #transform(Vector3dc, Vector3d) + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2d transformPosition(double x, double y, Vector2d dest); + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=0, by + * this matrix and store the result in that vector. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector2dc, Vector2d)}. + * + * @see #transformDirection(Vector2dc, Vector2d) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector2d transformDirection(Vector2d v); + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=0, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector2d)}. + * + * @see #transformDirection(Vector2d) + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + Vector2d transformDirection(Vector2dc v, Vector2d dest); + + /** + * Transform/multiply the given 2D-vector (x, y), as if it was a 3D-vector with z=0, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector2d)}. + * + * @see #transformDirection(Vector2d) + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2d transformDirection(double x, double y, Vector2d dest); + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d rotate(double ang, Matrix3x2d dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d rotateLocal(double ang, Matrix3x2d dest); + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians about + * the specified rotation center (x, y) and store the result in dest. + *

+ * This method is equivalent to calling: translate(x, y, dest).rotate(ang).translate(-x, -y) + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @see #translate(double, double, Matrix3x2d) + * @see #rotate(double, Matrix3x2d) + * + * @param ang + * the angle in radians + * @param x + * the x component of the rotation center + * @param y + * the y component of the rotation center + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d rotateAbout(double ang, double x, double y, Matrix3x2d dest); + + /** + * Apply a rotation transformation to this matrix that rotates the given normalized fromDir direction vector + * to point along the normalized toDir, and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param fromDir + * the normalized direction which should be rotate to point along toDir + * @param toDir + * the normalized destination direction + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d rotateTo(Vector2dc fromDir, Vector2dc toDir, Matrix3x2d dest); + + /** + * Apply a "view" transformation to this matrix that maps the given (left, bottom) and + * (right, top) corners to (-1, -1) and (1, 1) respectively and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + * + * @param left + * the distance from the center to the left view edge + * @param right + * the distance from the center to the right view edge + * @param bottom + * the distance from the center to the bottom view edge + * @param top + * the distance from the center to the top view edge + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2d view(double left, double right, double bottom, double top, Matrix3x2d dest); + + /** + * Obtain the position that gets transformed to the origin by this matrix. + * This can be used to get the position of the "camera" from a given view transformation matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2d inv = new Matrix3x2d(this).invertAffine();
+     * inv.transform(origin.set(0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + Vector2d origin(Vector2d origin); + + /** + * Obtain the extents of the view transformation of this matrix and store it in area. + * This can be used to determine which region of the screen (i.e. the NDC space) is covered by the view. + * + * @param area + * will hold the view area as [minX, minY, maxX, maxY] + * @return area + */ + double[] viewArea(double[] area); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 2x2 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2d inv = new Matrix3x2d(this).invert();
+     * inv.transformDirection(dir.set(1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector2d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector2d positiveX(Vector2d dir); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 2x2 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2d inv = new Matrix3x2d(this).transpose();
+     * inv.transformDirection(dir.set(1, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector2d normalizedPositiveX(Vector2d dir); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 2x2 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2d inv = new Matrix3x2d(this).invert();
+     * inv.transformDirection(dir.set(0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector2d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector2d positiveY(Vector2d dir); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 2x2 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2d inv = new Matrix3x2d(this).transpose();
+     * inv.transformDirection(dir.set(0, 1));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector2d normalizedPositiveY(Vector2d dir); + + /** + * Unproject the given window coordinates (winX, winY) by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix3x2d)} and then the method {@link #unprojectInv(double, double, int[], Vector2d) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(double, double, int[], Vector2d) + * @see #invert(Matrix3x2d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector2d unproject(double winX, double winY, int[] viewport, Vector2d dest); + + /** + * Unproject the given window coordinates (winX, winY) by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(double, double, int[], Vector2d) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + * + * @see #unproject(double, double, int[], Vector2d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector2d unprojectInv(double winX, double winY, int[] viewport, Vector2d dest); + + /** + * Test whether the given point (x, y) is within the frustum defined by this matrix. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given point with the coordinates (x, y, z) given + * in space M is within the clip space. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the x-coordinate of the point + * @param y + * the y-coordinate of the point + * @return true if the given point is inside the frustum; false otherwise + */ + boolean testPoint(double x, double y); + + /** + * Test whether the given circle is partly or completely within or outside of the frustum defined by this matrix. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given sphere with the coordinates (x, y, z) given + * in space M is within the clip space. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the x-coordinate of the circle's center + * @param y + * the y-coordinate of the circle's center + * @param r + * the circle's radius + * @return true if the given circle is partly or completely inside the frustum; false otherwise + */ + boolean testCircle(double x, double y, double r); + + /** + * Test whether the given axis-aligned rectangle is partly or completely within or outside of the frustum defined by this matrix. + * The rectangle is specified via its min and max corner coordinates. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given axis-aligned rectangle with its minimum corner coordinates (minX, minY, minZ) + * and maximum corner coordinates (maxX, maxY, maxZ) given in space M is within the clip space. + *

+ * Reference: Efficient View Frustum Culling + *
+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @return true if the axis-aligned box is completely or partly inside of the frustum; false otherwise + */ + boolean testAar(double minX, double minY, double maxX, double maxY); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix3x2dc m, double delta); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2f.java new file mode 100644 index 000000000..d090da368 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2f.java @@ -0,0 +1,2492 @@ +/* + * The MIT License + * + * Copyright (c) 2017-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + + +/** + * Contains the definition of a 3x2 matrix of floats, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10 m20
+ * m01 m11 m21
+ * + * @author Kai Burjack + */ +public class Matrix3x2f implements Matrix3x2fc, Externalizable, Cloneable { + + private static final long serialVersionUID = 1L; + + public float m00, m01; + public float m10, m11; + public float m20, m21; + + /** + * Create a new {@link Matrix3x2f} and set it to {@link #identity() identity}. + */ + public Matrix3x2f() { + this.m00 = 1.0f; + this.m11 = 1.0f; + } + + /** + * Create a new {@link Matrix3x2f} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix3x2fc} to copy the values from + */ + public Matrix3x2f(Matrix3x2fc mat) { + if (mat instanceof Matrix3x2f) { + MemUtil.INSTANCE.copy((Matrix3x2f) mat, this); + } else { + setMatrix3x2fc(mat); + } + } + + /** + * Create a new {@link Matrix3x2f} by setting its left 2x2 submatrix to the values of the given {@link Matrix2fc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix2fc} + */ + public Matrix3x2f(Matrix2fc mat) { + if (mat instanceof Matrix2f) { + MemUtil.INSTANCE.copy((Matrix2f) mat, this); + } else { + setMatrix2fc(mat); + } + } + + /** + * Create a new 3x2 matrix using the supplied float values. The order of the parameter is column-major, + * so the first two parameters specify the two elements of the first column. + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + * @param m20 + * the value of m20 + * @param m21 + * the value of m21 + */ + public Matrix3x2f(float m00, float m01, + float m10, float m11, + float m20, float m21) { + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + this.m20 = m20; + this.m21 = m21; + } + + /** + * Create a new {@link Matrix3x2f} by reading its 6 float components from the given {@link FloatBuffer} + * at the buffer's current position. + *

+ * That FloatBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link FloatBuffer} to read the matrix values from + */ + public Matrix3x2f(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + public float m00() { + return m00; + } + public float m01() { + return m01; + } + public float m10() { + return m10; + } + public float m11() { + return m11; + } + public float m20() { + return m20; + } + public float m21() { + return m21; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + Matrix3x2f _m00(float m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + Matrix3x2f _m01(float m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + Matrix3x2f _m10(float m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + Matrix3x2f _m11(float m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + Matrix3x2f _m20(float m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + Matrix3x2f _m21(float m21) { + this.m21 = m21; + return this; + } + + /** + * Set the elements of this matrix to the ones in m. + * + * @param m + * the matrix to copy the elements from + * @return this + */ + public Matrix3x2f set(Matrix3x2fc m) { + if (m instanceof Matrix3x2f) { + MemUtil.INSTANCE.copy((Matrix3x2f) m, this); + } else { + setMatrix3x2fc(m); + } + return this; + } + private void setMatrix3x2fc(Matrix3x2fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + m20 = mat.m20(); + m21 = mat.m21(); + } + + /** + * Set the left 2x2 submatrix of this {@link Matrix3x2f} to the given {@link Matrix2fc} and don't change the other elements. + * + * @param m + * the 2x2 matrix + * @return this + */ + public Matrix3x2f set(Matrix2fc m) { + if (m instanceof Matrix2f) { + MemUtil.INSTANCE.copy((Matrix2f) m, this); + } else { + setMatrix2fc(m); + } + return this; + } + private void setMatrix2fc(Matrix2fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m10 = mat.m10(); + m11 = mat.m11(); + } + + /** + * Multiply this matrix by the supplied right matrix by assuming a third row in + * both matrices of (0, 0, 1). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix3x2f mul(Matrix3x2fc right) { + return mul(right, this); + } + + /** + * Multiply this matrix by the supplied right matrix by assuming a third row in + * both matrices of (0, 0, 1) and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f mul(Matrix3x2fc right, Matrix3x2f dest) { + float nm00 = m00 * right.m00() + m10 * right.m01(); + float nm01 = m01 * right.m00() + m11 * right.m01(); + float nm10 = m00 * right.m10() + m10 * right.m11(); + float nm11 = m01 * right.m10() + m11 * right.m11(); + float nm20 = m00 * right.m20() + m10 * right.m21() + m20; + float nm21 = m01 * right.m20() + m11 * right.m21() + m21; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = nm20; + dest.m21 = nm21; + return dest; + } + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in this. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @return this + */ + public Matrix3x2f mulLocal(Matrix3x2fc left) { + return mulLocal(left, this); + } + + public Matrix3x2f mulLocal(Matrix3x2fc left, Matrix3x2f dest) { + float nm00 = left.m00() * m00 + left.m10() * m01; + float nm01 = left.m01() * m00 + left.m11() * m01; + float nm10 = left.m00() * m10 + left.m10() * m11; + float nm11 = left.m01() * m10 + left.m11() * m11; + float nm20 = left.m00() * m20 + left.m10() * m21 + left.m20(); + float nm21 = left.m01() * m20 + left.m11() * m21 + left.m21(); + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = nm20; + dest.m21 = nm21; + return dest; + } + + /** + * Set the values within this matrix to the supplied float values. The result looks like this: + *

+ * m00, m10, m20
+ * m01, m11, m21
+ * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @param m20 + * the new value of m20 + * @param m21 + * the new value of m21 + * @return this + */ + public Matrix3x2f set(float m00, float m01, + float m10, float m11, + float m20, float m21) { + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + this.m20 = m20; + this.m21 = m21; + return this; + } + + /** + * Set the values in this matrix based on the supplied float array. The result looks like this: + *

+ * 0, 2, 4
+ * 1, 3, 5
+ * + * This method only uses the first 6 values, all others are ignored. + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix3x2f set(float m[]) { + MemUtil.INSTANCE.copy(m, 0, this); + return this; + } + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + public float determinant() { + return m00 * m11 - m01 * m10; + } + + /** + * Invert this matrix by assuming a third row in this matrix of (0, 0, 1). + * + * @return this + */ + public Matrix3x2f invert() { + return invert(this); + } + + /** + * Invert the this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f invert(Matrix3x2f dest) { + // client must make sure that matrix is invertible + float s = 1.0f / (m00 * m11 - m01 * m10); + float nm00 = m11 * s; + float nm01 = -m01 * s; + float nm10 = -m10 * s; + float nm11 = m00 * s; + float nm20 = (m10 * m21 - m20 * m11) * s; + float nm21 = (m20 * m01 - m00 * m21) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = nm20; + dest.m21 = nm21; + return dest; + } + + /** + * Set this matrix to be a simple translation matrix in a two-dimensional coordinate system. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + *

+ * In order to apply a translation via to an already existing transformation + * matrix, use {@link #translate(float, float) translate()} instead. + * + * @see #translate(float, float) + * + * @param x + * the units to translate in x + * @param y + * the units to translate in y + * @return this + */ + public Matrix3x2f translation(float x, float y) { + m00 = 1.0f; + m01 = 0.0f; + m10 = 0.0f; + m11 = 1.0f; + m20 = x; + m21 = y; + return this; + } + + /** + * Set this matrix to be a simple translation matrix in a two-dimensional coordinate system. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + *

+ * In order to apply a translation via to an already existing transformation + * matrix, use {@link #translate(Vector2fc) translate()} instead. + * + * @see #translate(Vector2fc) + * + * @param offset + * the translation + * @return this + */ + public Matrix3x2f translation(Vector2fc offset) { + return translation(offset.x(), offset.y()); + } + + /** + * Set only the translation components of this matrix (m20, m21) to the given values (x, y). + *

+ * To build a translation matrix instead, use {@link #translation(float, float)}. + * To apply a translation to another matrix, use {@link #translate(float, float)}. + * + * @see #translation(float, float) + * @see #translate(float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @return this + */ + public Matrix3x2f setTranslation(float x, float y) { + m20 = x; + m21 = y; + return this; + } + + /** + * Set only the translation components of this matrix (m20, m21) to the given values (offset.x, offset.y). + *

+ * To build a translation matrix instead, use {@link #translation(Vector2fc)}. + * To apply a translation to another matrix, use {@link #translate(Vector2fc)}. + * + * @see #translation(Vector2fc) + * @see #translate(Vector2fc) + * + * @param offset + * the new translation to set + * @return this + */ + public Matrix3x2f setTranslation(Vector2f offset) { + return setTranslation(offset.x, offset.y); + } + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y and store the result + * in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(float, float)}. + * + * @see #translation(float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f translate(float x, float y, Matrix3x2f dest) { + float rm20 = x; + float rm21 = y; + dest.m20 = m00 * rm20 + m10 * rm21 + m20; + dest.m21 = m01 * rm20 + m11 * rm21 + m21; + dest.m00 = m00; + dest.m01 = m01; + dest.m10 = m10; + dest.m11 = m11; + return dest; + } + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(float, float)}. + * + * @see #translation(float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @return this + */ + public Matrix3x2f translate(float x, float y) { + return translate(x, y, this); + } + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y, and + * store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(float, float)}. + * + * @see #translation(Vector2fc) + * + * @param offset + * the offset to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f translate(Vector2fc offset, Matrix3x2f dest) { + return translate(offset.x(), offset.y(), dest); + } + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(float, float)}. + * + * @see #translation(Vector2fc) + * + * @param offset + * the offset to translate + * @return this + */ + public Matrix3x2f translate(Vector2fc offset) { + return translate(offset.x(), offset.y(), this); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector2fc)}. + * + * @see #translation(Vector2fc) + * + * @param offset + * the number of units in x and y by which to translate + * @return this + */ + public Matrix3x2f translateLocal(Vector2fc offset) { + return translateLocal(offset.x(), offset.y()); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector2fc)}. + * + * @see #translation(Vector2fc) + * + * @param offset + * the number of units in x and y by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f translateLocal(Vector2fc offset, Matrix3x2f dest) { + return translateLocal(offset.x(), offset.y(), dest); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(float, float)}. + * + * @see #translation(float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f translateLocal(float x, float y, Matrix3x2f dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m10 = m10; + dest.m11 = m11; + dest.m20 = m20 + x; + dest.m21 = m21 + y; + return dest; + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(float, float)}. + * + * @see #translation(float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @return this + */ + public Matrix3x2f translateLocal(float x, float y) { + return translateLocal(x, y, this); + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + " " + Runtime.format(m20, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + " " + Runtime.format(m21, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix3x2fc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix3x2fc) + * + * @param dest + * the destination matrix + * @return dest + */ + public Matrix3x2f get(Matrix3x2f dest) { + return dest.set(this); + } + + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public FloatBuffer get(FloatBuffer buffer) { + return get(buffer.position(), buffer); + } + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public ByteBuffer get(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get3x3(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x3(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public FloatBuffer get3x3(FloatBuffer buffer) { + MemUtil.INSTANCE.put3x3(this, 0, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public FloatBuffer get3x3(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put3x3(this, index, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get3x3(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x3(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public ByteBuffer get3x3(ByteBuffer buffer) { + MemUtil.INSTANCE.put3x3(this, 0, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public ByteBuffer get3x3(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put3x3(this, index, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get4x4(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public FloatBuffer get4x4(FloatBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, 0, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public FloatBuffer get4x4(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, index, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get4x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + public ByteBuffer get4x4(ByteBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, 0, buffer); + return buffer; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + public ByteBuffer get4x4(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, index, buffer); + return buffer; + } + public Matrix3x2fc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Store this matrix into the supplied float array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + public float[] get(float[] arr, int offset) { + MemUtil.INSTANCE.copy(this, arr, offset); + return arr; + } + + /** + * Store this matrix into the supplied float array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + public float[] get(float[] arr) { + return get(arr, 0); + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied float array at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + public float[] get3x3(float[] arr, int offset) { + MemUtil.INSTANCE.copy3x3(this, arr, offset); + return arr; + } + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied float array. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get3x3(float[], int)}. + * + * @see #get3x3(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + public float[] get3x3(float[] arr) { + return get3x3(arr, 0); + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied float array at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + public float[] get4x4(float[] arr, int offset) { + MemUtil.INSTANCE.copy4x4(this, arr, offset); + return arr; + } + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied float array. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get4x4(float[], int)}. + * + * @see #get4x4(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + public float[] get4x4(float[] arr) { + return get4x4(arr, 0); + } + + /** + * Set the values of this matrix by reading 6 float values from the given {@link FloatBuffer} in column-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2f set(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 6 float values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2f set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Set the values of this matrix by reading 6 float values from the given {@link FloatBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2f set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 6 float values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2f set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this matrix by reading 6 float values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix3x2f setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set all values within this matrix to zero. + * + * @return this + */ + public Matrix3x2f zero() { + MemUtil.INSTANCE.zero(this); + return this; + } + + /** + * Set this matrix to the identity. + * + * @return this + */ + public Matrix3x2f identity() { + MemUtil.INSTANCE.identity(this); + return this; + } + + /** + * Apply scaling to this matrix by scaling the unit axes by the given x and y and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f scale(float x, float y, Matrix3x2f dest) { + dest.m00 = m00 * x; + dest.m01 = m01 * x; + dest.m10 = m10 * y; + dest.m11 = m11 * y; + dest.m20 = m20; + dest.m21 = m21; + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x and y factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix3x2f scale(float x, float y) { + return scale(x, y, this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @return this + */ + public Matrix3x2f scale(Vector2fc xy) { + return scale(xy.x(), xy.y(), this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f scale(Vector2fc xy, Matrix3x2f dest) { + return scale(xy.x(), xy.y(), dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling the two base axes by the given xy factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @see #scale(float, float, Matrix3x2f) + * + * @param xy + * the factor for the two components + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f scale(float xy, Matrix3x2f dest) { + return scale(xy, xy, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling the two base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @see #scale(float, float) + * + * @param xy + * the factor for the two components + * @return this + */ + public Matrix3x2f scale(float xy) { + return scale(xy, xy); + } + + public Matrix3x2f scaleLocal(float x, float y, Matrix3x2f dest) { + dest.m00 = x * m00; + dest.m01 = y * m01; + dest.m10 = x * m10; + dest.m11 = y * m11; + dest.m20 = x * m20; + dest.m21 = y * m21; + return dest; + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x and y factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix3x2f scaleLocal(float x, float y) { + return scaleLocal(x, y, this); + } + + public Matrix3x2f scaleLocal(float xy, Matrix3x2f dest) { + return scaleLocal(xy, xy, dest); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given xy factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param xy + * the factor of the x and y component + * @return this + */ + public Matrix3x2f scaleLocal(float xy) { + return scaleLocal(xy, xy, this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using (ox, oy) as the scaling origin, and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, dest).scale(sx, sy).translate(-ox, -oy) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f scaleAround(float sx, float sy, float ox, float oy, Matrix3x2f dest) { + float nm20 = m00 * ox + m10 * oy + m20; + float nm21 = m01 * ox + m11 * oy + m21; + dest.m00 = m00 * sx; + dest.m01 = m01 * sx; + dest.m10 = m10 * sy; + dest.m11 = m11 * sy; + dest.m20 = dest.m00 * -ox + dest.m10 * -oy + nm20; + dest.m21 = dest.m01 * -ox + dest.m11 * -oy + nm21; + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using (ox, oy) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy).scale(sx, sy).translate(-ox, -oy) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @return this + */ + public Matrix3x2f scaleAround(float sx, float sy, float ox, float oy) { + return scaleAround(sx, sy, ox, oy, this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, dest).scale(factor).translate(-ox, -oy) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + public Matrix3x2f scaleAround(float factor, float ox, float oy, Matrix3x2f dest) { + return scaleAround(factor, factor, ox, oy, this); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy).scale(factor).translate(-ox, -oy) + * + * @param factor + * the scaling factor for all axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @return this + */ + public Matrix3x2f scaleAround(float factor, float ox, float oy) { + return scaleAround(factor, factor, ox, oy, this); + } + + public Matrix3x2f scaleAroundLocal(float sx, float sy, float ox, float oy, Matrix3x2f dest) { + dest.m00 = sx * m00; + dest.m01 = sy * m01; + dest.m10 = sx * m10; + dest.m11 = sy * m11; + dest.m20 = sx * m20 - sx * ox + ox; + dest.m21 = sy * m21 - sy * oy + oy; + return dest; + } + + public Matrix3x2f scaleAroundLocal(float factor, float ox, float oy, Matrix3x2f dest) { + return scaleAroundLocal(factor, factor, ox, oy, dest); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using (ox, oy) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix3x2f().translate(ox, oy).scale(sx, sy).translate(-ox, -oy).mul(this, this) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix3x2f scaleAroundLocal(float sx, float sy, float sz, float ox, float oy, float oz) { + return scaleAroundLocal(sx, sy, ox, oy, this); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix3x2f().translate(ox, oy).scale(factor).translate(-ox, -oy).mul(this, this) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @return this + */ + public Matrix3x2f scaleAroundLocal(float factor, float ox, float oy) { + return scaleAroundLocal(factor, factor, ox, oy, this); + } + + /** + * Set this matrix to be a simple scale matrix, which scales the two base axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a matrix, use {@link #scale(float) scale()} instead. + * + * @see #scale(float) + * + * @param factor + * the scale factor in x and y + * @return this + */ + public Matrix3x2f scaling(float factor) { + return scaling(factor, factor); + } + + /** + * Set this matrix to be a simple scale matrix. + * + * @param x + * the scale in x + * @param y + * the scale in y + * @return this + */ + public Matrix3x2f scaling(float x, float y) { + m00 = x; + m01 = 0.0f; + m10 = 0.0f; + m11 = y; + m20 = 0.0f; + m21 = 0.0f; + return this; + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(float) rotate()} instead. + * + * @see #rotate(float) + * + * @param angle + * the angle in radians + * @return this + */ + public Matrix3x2f rotation(float angle) { + float cos = Math.cos(angle); + float sin = Math.sin(angle); + m00 = cos; + m10 = -sin; + m20 = 0.0f; + m01 = sin; + m11 = cos; + m21 = 0.0f; + return this; + } + + /** + * Transform/multiply the given vector by this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in that vector. + * + * @see Vector3f#mul(Matrix3x2fc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + public Vector3f transform(Vector3f v) { + return v.mul(this); + } + + /** + * Transform/multiply the given vector by this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in dest. + * + * @see Vector3f#mul(Matrix3x2fc, Vector3f) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + public Vector3f transform(Vector3f v, Vector3f dest) { + return v.mul(this, dest); + } + + /** + * Transform/multiply the given vector (x, y, z) by this matrix and store the result in dest. + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param z + * the z component of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + public Vector3f transform(float x, float y, float z, Vector3f dest) { + return dest.set(m00 * x + m10 * y + m20 * z, m01 * x + m11 * y + m21 * z, z); + } + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=1, by + * this matrix and store the result in that vector. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in another vector, use {@link #transformPosition(Vector2fc, Vector2f)}. + * + * @see #transformPosition(Vector2fc, Vector2f) + * @see #transform(Vector3f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + public Vector2f transformPosition(Vector2f v) { + v.set(m00 * v.x + m10 * v.y + m20, + m01 * v.x + m11 * v.y + m21); + return v; + } + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=1, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector2f)}. + * + * @see #transformPosition(Vector2f) + * @see #transform(Vector3f, Vector3f) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector2f transformPosition(Vector2fc v, Vector2f dest) { + dest.set(m00 * v.x() + m10 * v.y() + m20, + m01 * v.x() + m11 * v.y() + m21); + return dest; + } + + /** + * Transform/multiply the given 2D-vector (x, y), as if it was a 3D-vector with z=1, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector2f)}. + * + * @see #transformPosition(Vector2f) + * @see #transform(Vector3f, Vector3f) + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector2f transformPosition(float x, float y, Vector2f dest) { + return dest.set(m00 * x + m10 * y + m20, m01 * x + m11 * y + m21); + } + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=0, by + * this matrix and store the result in that vector. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector2fc, Vector2f)}. + * + * @see #transformDirection(Vector2fc, Vector2f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + public Vector2f transformDirection(Vector2f v) { + v.set(m00 * v.x + m10 * v.y, + m01 * v.x + m11 * v.y); + return v; + } + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=0, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector2f)}. + * + * @see #transformDirection(Vector2f) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector2f transformDirection(Vector2fc v, Vector2f dest) { + dest.set(m00 * v.x() + m10 * v.y(), + m01 * v.x() + m11 * v.y()); + return dest; + } + + /** + * Transform/multiply the given 2D-vector (x, y), as if it was a 3D-vector with z=0, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector2f)}. + * + * @see #transformDirection(Vector2f) + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + public Vector2f transformDirection(float x, float y, Vector2f dest) { + return dest.set(m00 * x + m10 * y, m01 * x + m11 * y); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(m00); + out.writeFloat(m01); + out.writeFloat(m10); + out.writeFloat(m11); + out.writeFloat(m20); + out.writeFloat(m21); + } + + public void readExternal(ObjectInput in) throws IOException { + m00 = in.readFloat(); + m01 = in.readFloat(); + m10 = in.readFloat(); + m11 = in.readFloat(); + m20 = in.readFloat(); + m21 = in.readFloat(); + } + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix3x2f rotate(float ang) { + return rotate(ang, this); + } + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f rotate(float ang, Matrix3x2f dest) { + float cos = Math.cos(ang); + float sin = Math.sin(ang); + float rm00 = cos; + float rm01 = sin; + float rm10 = -sin; + float rm11 = cos; + float nm00 = m00 * rm00 + m10 * rm01; + float nm01 = m01 * rm00 + m11 * rm01; + dest.m10 = m00 * rm10 + m10 * rm11; + dest.m11 = m01 * rm10 + m11 * rm11; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m20 = m20; + dest.m21 = m21; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float) + * + * @param ang + * the angle in radians to rotate + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f rotateLocal(float ang, Matrix3x2f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm00 = cos * m00 - sin * m01; + float nm01 = sin * m00 + cos * m01; + float nm10 = cos * m10 - sin * m11; + float nm11 = sin * m10 + cos * m11; + float nm20 = cos * m20 - sin * m21; + float nm21 = sin * m20 + cos * m21; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = nm20; + dest.m21 = nm21; + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float) + * + * @param ang + * the angle in radians to rotate + * @return this + */ + public Matrix3x2f rotateLocal(float ang) { + return rotateLocal(ang, this); + } + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians about + * the specified rotation center (x, y). + *

+ * This method is equivalent to calling: translate(x, y).rotate(ang).translate(-x, -y) + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @see #translate(float, float) + * @see #rotate(float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the rotation center + * @param y + * the y component of the rotation center + * @return this + */ + public Matrix3x2f rotateAbout(float ang, float x, float y) { + return rotateAbout(ang, x, y, this); + } + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians about + * the specified rotation center (x, y) and store the result in dest. + *

+ * This method is equivalent to calling: translate(x, y, dest).rotate(ang).translate(-x, -y) + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @see #translate(float, float, Matrix3x2f) + * @see #rotate(float, Matrix3x2f) + * + * @param ang + * the angle in radians + * @param x + * the x component of the rotation center + * @param y + * the y component of the rotation center + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f rotateAbout(float ang, float x, float y, Matrix3x2f dest) { + float tm20 = m00 * x + m10 * y + m20; + float tm21 = m01 * x + m11 * y + m21; + float cos = Math.cos(ang); + float sin = Math.sin(ang); + float nm00 = m00 * cos + m10 * sin; + float nm01 = m01 * cos + m11 * sin; + dest.m10 = m00 * -sin + m10 * cos; + dest.m11 = m01 * -sin + m11 * cos; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m20 = dest.m00 * -x + dest.m10 * -y + tm20; + dest.m21 = dest.m01 * -x + dest.m11 * -y + tm21; + return dest; + } + + /** + * Apply a rotation transformation to this matrix that rotates the given normalized fromDir direction vector + * to point along the normalized toDir, and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param fromDir + * the normalized direction which should be rotate to point along toDir + * @param toDir + * the normalized destination direction + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f rotateTo(Vector2fc fromDir, Vector2fc toDir, Matrix3x2f dest) { + float dot = fromDir.x() * toDir.x() + fromDir.y() * toDir.y(); + float det = fromDir.x() * toDir.y() - fromDir.y() * toDir.x(); + float rm00 = dot; + float rm01 = det; + float rm10 = -det; + float rm11 = dot; + float nm00 = m00 * rm00 + m10 * rm01; + float nm01 = m01 * rm00 + m11 * rm01; + dest.m10 = m00 * rm10 + m10 * rm11; + dest.m11 = m01 * rm10 + m11 * rm11; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m20 = m20; + dest.m21 = m21; + return dest; + } + + /** + * Apply a rotation transformation to this matrix that rotates the given normalized fromDir direction vector + * to point along the normalized toDir. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param fromDir + * the normalized direction which should be rotate to point along toDir + * @param toDir + * the normalized destination direction + * @return this + */ + public Matrix3x2f rotateTo(Vector2fc fromDir, Vector2fc toDir) { + return rotateTo(fromDir, toDir, this); + } + + /** + * Apply a "view" transformation to this matrix that maps the given (left, bottom) and + * (right, top) corners to (-1, -1) and (1, 1) respectively and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + * + * @see #setView(float, float, float, float) + * + * @param left + * the distance from the center to the left view edge + * @param right + * the distance from the center to the right view edge + * @param bottom + * the distance from the center to the bottom view edge + * @param top + * the distance from the center to the top view edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f view(float left, float right, float bottom, float top, Matrix3x2f dest) { + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm20 = (left + right) / (left - right); + float rm21 = (bottom + top) / (bottom - top); + dest.m20 = m00 * rm20 + m10 * rm21 + m20; + dest.m21 = m01 * rm20 + m11 * rm21 + m21; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + return dest; + } + + /** + * Apply a "view" transformation to this matrix that maps the given (left, bottom) and + * (right, top) corners to (-1, -1) and (1, 1) respectively. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + * + * @see #setView(float, float, float, float) + * + * @param left + * the distance from the center to the left view edge + * @param right + * the distance from the center to the right view edge + * @param bottom + * the distance from the center to the bottom view edge + * @param top + * the distance from the center to the top view edge + * @return this + */ + public Matrix3x2f view(float left, float right, float bottom, float top) { + return view(left, right, bottom, top, this); + } + + /** + * Set this matrix to define a "view" transformation that maps the given (left, bottom) and + * (right, top) corners to (-1, -1) and (1, 1) respectively. + * + * @see #view(float, float, float, float) + * + * @param left + * the distance from the center to the left view edge + * @param right + * the distance from the center to the right view edge + * @param bottom + * the distance from the center to the bottom view edge + * @param top + * the distance from the center to the top view edge + * @return this + */ + public Matrix3x2f setView(float left, float right, float bottom, float top) { + m00 = 2.0f / (right - left); + m01 = 0.0f; + m10 = 0.0f; + m11 = 2.0f / (top - bottom); + m20 = (left + right) / (left - right); + m21 = (bottom + top) / (bottom - top); + return this; + } + + /** + * Obtain the position that gets transformed to the origin by this matrix. + * This can be used to get the position of the "camera" from a given view transformation matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2f inv = new Matrix3x2f(this).invert();
+     * inv.transform(origin.set(0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + public Vector2f origin(Vector2f origin) { + float s = 1.0f / (m00 * m11 - m01 * m10); + origin.x = (m10 * m21 - m20 * m11) * s; + origin.y = (m20 * m01 - m00 * m21) * s; + return origin; + } + + /** + * Obtain the extents of the view transformation of this matrix and store it in area. + * This can be used to determine which region of the screen (i.e. the NDC space) is covered by the view. + * + * @param area + * will hold the view area as [minX, minY, maxX, maxY] + * @return area + */ + public float[] viewArea(float[] area) { + float s = 1.0f / (m00 * m11 - m01 * m10); + float rm00 = m11 * s; + float rm01 = -m01 * s; + float rm10 = -m10 * s; + float rm11 = m00 * s; + float rm20 = (m10 * m21 - m20 * m11) * s; + float rm21 = (m20 * m01 - m00 * m21) * s; + float nxnyX = -rm00 - rm10; + float nxnyY = -rm01 - rm11; + float pxnyX = rm00 - rm10; + float pxnyY = rm01 - rm11; + float nxpyX = -rm00 + rm10; + float nxpyY = -rm01 + rm11; + float pxpyX = rm00 + rm10; + float pxpyY = rm01 + rm11; + float minX = nxnyX; + minX = minX < nxpyX ? minX : nxpyX; + minX = minX < pxnyX ? minX : pxnyX; + minX = minX < pxpyX ? minX : pxpyX; + float minY = nxnyY; + minY = minY < nxpyY ? minY : nxpyY; + minY = minY < pxnyY ? minY : pxnyY; + minY = minY < pxpyY ? minY : pxpyY; + float maxX = nxnyX; + maxX = maxX > nxpyX ? maxX : nxpyX; + maxX = maxX > pxnyX ? maxX : pxnyX; + maxX = maxX > pxpyX ? maxX : pxpyX; + float maxY = nxnyY; + maxY = maxY > nxpyY ? maxY : nxpyY; + maxY = maxY > pxnyY ? maxY : pxnyY; + maxY = maxY > pxpyY ? maxY : pxpyY; + area[0] = minX + rm20; + area[1] = minY + rm21; + area[2] = maxX + rm20; + area[3] = maxY + rm21; + return area; + } + + public Vector2f positiveX(Vector2f dir) { + float s = m00 * m11 - m01 * m10; + s = 1.0f / s; + dir.x = m11 * s; + dir.y = -m01 * s; + return dir.normalize(dir); + } + + public Vector2f normalizedPositiveX(Vector2f dir) { + dir.x = m11; + dir.y = -m01; + return dir; + } + + public Vector2f positiveY(Vector2f dir) { + float s = m00 * m11 - m01 * m10; + s = 1.0f / s; + dir.x = -m10 * s; + dir.y = m00 * s; + return dir.normalize(dir); + } + + public Vector2f normalizedPositiveY(Vector2f dir) { + dir.x = -m10; + dir.y = m00; + return dir; + } + + /** + * Unproject the given window coordinates (winX, winY) by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix3x2f)} and then the method {@link #unprojectInv(float, float, int[], Vector2f) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(float, float, int[], Vector2f) + * @see #invert(Matrix3x2f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + public Vector2f unproject(float winX, float winY, int[] viewport, Vector2f dest) { + float s = 1.0f / (m00 * m11 - m01 * m10); + float im00 = m11 * s; + float im01 = -m01 * s; + float im10 = -m10 * s; + float im11 = m00 * s; + float im20 = (m10 * m21 - m20 * m11) * s; + float im21 = (m20 * m01 - m00 * m21) * s; + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + dest.x = im00 * ndcX + im10 * ndcY + im20; + dest.y = im01 * ndcX + im11 * ndcY + im21; + return dest; + } + + /** + * Unproject the given window coordinates (winX, winY) by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(float, float, int[], Vector2f) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + * + * @see #unproject(float, float, int[], Vector2f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + public Vector2f unprojectInv(float winX, float winY, int[] viewport, Vector2f dest) { + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + dest.x = m00 * ndcX + m10 * ndcY + m20; + dest.y = m01 * ndcX + m11 * ndcY + m21; + return dest; + } + + /** + * Apply shearing to this matrix by shearing along the X axis using the Y axis factor yFactor. + * + * @param yFactor + * the factor for the Y component to shear along the X axis + * @return this + */ + public Matrix3x2f shearX(float yFactor) { + return shearX(yFactor, this); + } + + /** + * Apply shearing to this matrix by shearing along the X axis using the Y axis factor yFactor, + * and store the result in dest. + * + * @param yFactor + * the factor for the Y component to shear along the X axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f shearX(float yFactor, Matrix3x2f dest) { + float nm10 = m00 * yFactor + m10; + float nm11 = m01 * yFactor + m11; + dest.m00 = m00; + dest.m01 = m01; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m20 = m20; + dest.m21 = m21; + return dest; + } + + /** + * Apply shearing to this matrix by shearing along the Y axis using the X axis factor xFactor. + * + * @param xFactor + * the factor for the X component to shear along the Y axis + * @return this + */ + public Matrix3x2f shearY(float xFactor) { + return shearY(xFactor, this); + } + + /** + * Apply shearing to this matrix by shearing along the Y axis using the X axis factor xFactor, + * and store the result in dest. + * + * @param xFactor + * the factor for the X component to shear along the Y axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix3x2f shearY(float xFactor, Matrix3x2f dest) { + float nm00 = m00 + m10 * xFactor; + float nm01 = m01 + m11 * xFactor; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m10 = m10; + dest.m11 = m11; + dest.m20 = m20; + dest.m21 = m21; + return dest; + } + + /** + * Compute the extents of the coordinate system before this transformation was applied and store the resulting + * corner coordinates in corner and the span vectors in xDir and yDir. + *

+ * That means, given the maximum extents of the coordinate system between [-1..+1] in all dimensions, + * this method returns one corner and the length and direction of the two base axis vectors in the coordinate + * system before this transformation is applied, which transforms into the corner coordinates [-1, +1]. + * + * @param corner + * will hold one corner of the span + * @param xDir + * will hold the direction and length of the span along the positive X axis + * @param yDir + * will hold the direction and length of the span along the positive Y axis + * @return this + */ + public Matrix3x2f span(Vector2f corner, Vector2f xDir, Vector2f yDir) { + float s = 1.0f / (m00 * m11 - m01 * m10); + float nm00 = m11 * s, nm01 = -m01 * s, nm10 = -m10 * s, nm11 = m00 * s; + corner.x = -nm00 - nm10 + (m10 * m21 - m20 * m11) * s; + corner.y = -nm01 - nm11 + (m20 * m01 - m00 * m21) * s; + xDir.x = 2.0f * nm00; xDir.y = 2.0f * nm01; + yDir.x = 2.0f * nm10; yDir.y = 2.0f * nm11; + return this; + } + + public boolean testPoint(float x, float y) { + float nxX = +m00, nxY = +m10, nxW = 1.0f + m20; + float pxX = -m00, pxY = -m10, pxW = 1.0f - m20; + float nyX = +m01, nyY = +m11, nyW = 1.0f + m21; + float pyX = -m01, pyY = -m11, pyW = 1.0f - m21; + return nxX * x + nxY * y + nxW >= 0 && pxX * x + pxY * y + pxW >= 0 && + nyX * x + nyY * y + nyW >= 0 && pyX * x + pyY * y + pyW >= 0; + } + + public boolean testCircle(float x, float y, float r) { + float invl; + float nxX = +m00, nxY = +m10, nxW = 1.0f + m20; + invl = Math.invsqrt(nxX * nxX + nxY * nxY); + nxX *= invl; nxY *= invl; nxW *= invl; + float pxX = -m00, pxY = -m10, pxW = 1.0f - m20; + invl = Math.invsqrt(pxX * pxX + pxY * pxY); + pxX *= invl; pxY *= invl; pxW *= invl; + float nyX = +m01, nyY = +m11, nyW = 1.0f + m21; + invl = Math.invsqrt(nyX * nyX + nyY * nyY); + nyX *= invl; nyY *= invl; nyW *= invl; + float pyX = -m01, pyY = -m11, pyW = 1.0f - m21; + invl = Math.invsqrt(pyX * pyX + pyY * pyY); + pyX *= invl; pyY *= invl; pyW *= invl; + return nxX * x + nxY * y + nxW >= -r && pxX * x + pxY * y + pxW >= -r && + nyX * x + nyY * y + nyW >= -r && pyX * x + pyY * y + pyW >= -r; + } + + public boolean testAar(float minX, float minY, float maxX, float maxY) { + float nxX = +m00, nxY = +m10, nxW = 1.0f + m20; + float pxX = -m00, pxY = -m10, pxW = 1.0f - m20; + float nyX = +m01, nyY = +m11, nyW = 1.0f + m21; + float pyX = -m01, pyY = -m11, pyW = 1.0f - m21; + /* + * This is an implementation of the "2.4 Basic intersection test" of the mentioned site. + * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted. + */ + return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) >= -nxW && + pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) >= -pxW && + nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) >= -nyW && + pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) >= -pyW; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(m00); + result = prime * result + Float.floatToIntBits(m01); + result = prime * result + Float.floatToIntBits(m10); + result = prime * result + Float.floatToIntBits(m11); + result = prime * result + Float.floatToIntBits(m20); + result = prime * result + Float.floatToIntBits(m21); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Matrix3x2f other = (Matrix3x2f) obj; + if (Float.floatToIntBits(m00) != Float.floatToIntBits(other.m00)) + return false; + if (Float.floatToIntBits(m01) != Float.floatToIntBits(other.m01)) + return false; + if (Float.floatToIntBits(m10) != Float.floatToIntBits(other.m10)) + return false; + if (Float.floatToIntBits(m11) != Float.floatToIntBits(other.m11)) + return false; + if (Float.floatToIntBits(m20) != Float.floatToIntBits(other.m20)) + return false; + if (Float.floatToIntBits(m21) != Float.floatToIntBits(other.m21)) + return false; + return true; + } + + public boolean equals(Matrix3x2fc m, float delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix3x2f)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + if (!Runtime.equals(m20, m.m20(), delta)) + return false; + if (!Runtime.equals(m21, m.m21(), delta)) + return false; + return true; + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && + Math.isFinite(m10) && Math.isFinite(m11) && + Math.isFinite(m20) && Math.isFinite(m21); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2fStack.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2fStack.java new file mode 100644 index 000000000..349f3260e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2fStack.java @@ -0,0 +1,186 @@ +/* + * The MIT License + * + * Copyright (c) 2018-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A stack of many {@link Matrix3x2f} instances. This resembles the matrix stack known from legacy OpenGL. + *

+ * This {@link Matrix3x2fStack} class inherits from {@link Matrix3x2f}, so the current/top matrix is always the + * {@link Matrix3x2fStack}/{@link Matrix3x2f} itself. This affects all operations in {@link Matrix3x2f} that take + * another {@link Matrix3x2f} as parameter. If a {@link Matrix3x2fStack} is used as argument to those methods, the + * effective argument will always be the current matrix of the matrix stack. + * + * @author Kai Burjack + */ +public class Matrix3x2fStack extends Matrix3x2f { + + private static final long serialVersionUID = 1L; + + /** + * The matrix stack as a non-growable array. The size of the stack must be specified in the {@link #Matrix3x2fStack(int) constructor}. + */ + private Matrix3x2f[] mats; + + /** + * The index of the "current" matrix within {@link #mats}. + */ + private int curr; + + /** + * Create a new {@link Matrix3x2fStack} of the given size. + *

+ * Initially the stack pointer is at zero and the current matrix is set to identity. + * + * @param stackSize + * the size of the stack. This must be at least 1, in which case the {@link Matrix3x2fStack} simply only consists of this + * {@link Matrix3x2f} + */ + public Matrix3x2fStack(int stackSize) { + if (stackSize < 1) { + throw new IllegalArgumentException("stackSize must be >= 1"); //$NON-NLS-1$ + } + mats = new Matrix3x2f[stackSize - 1]; + // Allocate all matrices up front to keep the promise of being "allocation-free" + for (int i = 0; i < mats.length; i++) { + mats[i] = new Matrix3x2f(); + } + } + + /** + * Do not invoke manually! Only meant for serialization. + *

+ * Invoking this constructor from client code will result in an inconsistent state of the + * created {@link Matrix3x2fStack} instance. + */ + public Matrix3x2fStack() { + /* Empty! */ + } + + /** + * Set the stack pointer to zero and set the current/bottom matrix to {@link #identity() identity}. + * + * @return this + */ + public Matrix3x2fStack clear() { + curr = 0; + identity(); + return this; + } + + /** + * Increment the stack pointer by one and set the values of the new current matrix to the one directly below it. + * + * @return this + */ + public Matrix3x2fStack pushMatrix() { + if (curr == mats.length) { + throw new IllegalStateException("max stack size of " + (curr + 1) + " reached"); //$NON-NLS-1$ //$NON-NLS-2$ + } + mats[curr++].set(this); + return this; + } + + /** + * Decrement the stack pointer by one. + *

+ * This will effectively dispose of the current matrix. + * + * @return this + */ + public Matrix3x2fStack popMatrix() { + if (curr == 0) { + throw new IllegalStateException("already at the bottom of the stack"); //$NON-NLS-1$ + } + set(mats[--curr]); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + curr; + for (int i = 0; i < curr; i++) { + result = prime * result + mats[i].hashCode(); + } + return result; + } + + /* + * Contract between Matrix3x2f and Matrix3x2fStack: + * + * - Matrix3x2f.equals(Matrix3x2fStack) is true iff all the 6 matrix elements are equal + * - Matrix3x2fStack.equals(Matrix3x2f) is true iff all the 6 matrix elements are equal + * - Matrix3x2fStack.equals(Matrix3x2fStack) is true iff all 6 matrix elements are equal AND the matrix arrays as well as the stack pointer are equal + * - everything else is inequal + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (obj instanceof Matrix3x2fStack) { + Matrix3x2fStack other = (Matrix3x2fStack) obj; + if (curr != other.curr) + return false; + for (int i = 0; i < curr; i++) { + if (!mats[i].equals(other.mats[i])) + return false; + } + } + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(curr); + for (int i = 0; i < curr; i++) { + out.writeObject(mats[i]); + } + } + + public void readExternal(ObjectInput in) throws IOException { + super.readExternal(in); + curr = in.readInt(); + mats = new Matrix3x2fStack[curr]; + for (int i = 0; i < curr; i++) { + Matrix3x2f m = new Matrix3x2f(); + m.readExternal(in); + mats[i] = m; + } + } + + public Object clone() throws CloneNotSupportedException { + Matrix3x2fStack cloned = (Matrix3x2fStack) super.clone(); + Matrix3x2f[] clonedMats = new Matrix3x2f[mats.length]; + for (int i = 0; i < mats.length; i++) + clonedMats[i] = (Matrix3x2f) mats[i].clone(); + cloned.mats = clonedMats; + return cloned; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2fc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2fc.java new file mode 100644 index 000000000..833f93cec --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix3x2fc.java @@ -0,0 +1,1180 @@ +/* + * The MIT License + * + * Copyright (c) 2017-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + + +/** + * Interface to a read-only view of a 3x2 matrix of single-precision floats. + * + * @author Kai Burjack + */ +public interface Matrix3x2fc { + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + float m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + float m01(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + float m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + float m11(); + + /** + * Return the value of the matrix element at column 2 and row 0. + * + * @return the value of the matrix element + */ + float m20(); + + /** + * Return the value of the matrix element at column 2 and row 1. + * + * @return the value of the matrix element + */ + float m21(); + + /** + * Multiply this matrix by the supplied right matrix by assuming a third row in + * both matrices of (0, 0, 1) and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f mul(Matrix3x2fc right, Matrix3x2f dest); + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in dest. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix3x2f mulLocal(Matrix3x2fc left, Matrix3x2f dest); + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + float determinant(); + + /** + * Invert the this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f invert(Matrix3x2f dest); + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y and store the result + * in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f translate(float x, float y, Matrix3x2f dest); + + /** + * Apply a translation to this matrix by translating by the given number of units in x and y, and + * store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param offset + * the offset to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f translate(Vector2fc offset, Matrix3x2f dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param offset + * the number of units in x and y by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f translateLocal(Vector2fc offset, Matrix3x2f dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x and y and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f translateLocal(float x, float y, Matrix3x2f dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return dest + */ + Matrix3x2f get(Matrix3x2f dest); + + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get3x3(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x3(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get3x3(FloatBuffer buffer); + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get3x3(int index, FloatBuffer buffer); + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get3x3(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x3(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get3x3(ByteBuffer buffer); + + /** + * Store this matrix as an equivalent 3x3 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get3x3(int index, ByteBuffer buffer); + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get4x4(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get4x4(FloatBuffer buffer); + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get4x4(int index, FloatBuffer buffer); + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get4x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get4x4(ByteBuffer buffer); + + /** + * Store this matrix as an equivalent 4x4 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get4x4(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix3x2fc getToAddress(long address); + + /** + * Store this matrix into the supplied float array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get(float[] arr, int offset); + + /** + * Store this matrix into the supplied float array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get(float[] arr); + + /** + * Store this matrix as an equivalent 3x3 matrix into the supplied float array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get3x3(float[] arr, int offset); + + /** + * Store this matrix as an equivalent 3x3 matrix into the supplied float array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get3x3(float[], int)}. + * + * @see #get3x3(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get3x3(float[] arr); + + /** + * Store this matrix as an equivalent 4x4 matrix into the supplied float array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get4x4(float[] arr, int offset); + + /** + * Store this matrix as an equivalent 4x4 matrix into the supplied float array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get4x4(float[], int)}. + * + * @see #get4x4(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get4x4(float[] arr); + + /** + * Apply scaling to this matrix by scaling the unit axes by the given x and y and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f scale(float x, float y, Matrix3x2f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xy factors + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @param xy + * the factors of the x and y component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f scale(Vector2fc xy, Matrix3x2f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using the given (ox, oy) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix3x2f().translate(ox, oy).scale(sx, sy).translate(-ox, -oy).mul(this, dest) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f scaleAroundLocal(float sx, float sy, float ox, float oy, Matrix3x2f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix3x2f().translate(ox, oy).scale(factor).translate(-ox, -oy).mul(this, dest) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix3x2f scaleAroundLocal(float factor, float ox, float oy, Matrix3x2f dest); + + /** + * Apply scaling to this matrix by uniformly scaling the two base axes by the given xy factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the scaling will be applied first! + * + * @see #scale(float, float, Matrix3x2f) + * + * @param xy + * the factor for the two components + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f scale(float xy, Matrix3x2f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the two base axes by the given xy factor, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param xy + * the factor to scale all two base axes by + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f scaleLocal(float xy, Matrix3x2f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x and y + * factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f scaleLocal(float x, float y, Matrix3x2f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx and + * sy factors while using (ox, oy) as the scaling origin, and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, dest).scale(sx, sy).translate(-ox, -oy) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f scaleAround(float sx, float sy, float ox, float oy, Matrix3x2f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given factor + * while using (ox, oy) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, dest).scale(factor).translate(-ox, -oy) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix3x2f scaleAround(float factor, float ox, float oy, Matrix3x2f dest); + + /** + * Transform/multiply the given vector by this matrix by assuming a third row in this matrix of (0, 0, 1) + * and store the result in that vector. + * + * @see Vector3f#mul(Matrix3x2fc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3f transform(Vector3f v); + + /** + * Transform/multiply the given vector by this matrix and store the result in dest. + * + * @see Vector3f#mul(Matrix3x2fc, Vector3f) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3f transform(Vector3f v, Vector3f dest); + + /** + * Transform/multiply the given vector (x, y, z) by this matrix and store the result in dest. + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param z + * the z component of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3f transform(float x, float y, float z, Vector3f dest); + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=1, by + * this matrix and store the result in that vector. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in another vector, use {@link #transformPosition(Vector2fc, Vector2f)}. + * + * @see #transformPosition(Vector2fc, Vector2f) + * @see #transform(Vector3f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector2f transformPosition(Vector2f v); + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=1, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector2f)}. + * + * @see #transformPosition(Vector2f) + * @see #transform(Vector3f, Vector3f) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2f transformPosition(Vector2fc v, Vector2f dest); + + /** + * Transform/multiply the given 2D-vector (x, y), as if it was a 3D-vector with z=1, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 1.0, so it + * will represent a position/location in 2D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector2f)}. + * + * @see #transformPosition(Vector2f) + * @see #transform(Vector3f, Vector3f) + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2f transformPosition(float x, float y, Vector2f dest); + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=0, by + * this matrix and store the result in that vector. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector2fc, Vector2f)}. + * + * @see #transformDirection(Vector2fc, Vector2f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector2f transformDirection(Vector2f v); + + /** + * Transform/multiply the given 2D-vector, as if it was a 3D-vector with z=0, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector2f)}. + * + * @see #transformDirection(Vector2f) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2f transformDirection(Vector2fc v, Vector2f dest); + + /** + * Transform/multiply the given 2D-vector (x, y), as if it was a 3D-vector with z=0, by + * this matrix and store the result in dest. + *

+ * The given 2D-vector is treated as a 3D-vector with its z-component being 0.0, so it + * will represent a direction in 2D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector2f)}. + * + * @see #transformDirection(Vector2f) + * + * @param x + * the x component of the vector to transform + * @param y + * the y component of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector2f transformDirection(float x, float y, Vector2f dest); + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f rotate(float ang, Matrix3x2f dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f rotateLocal(float ang, Matrix3x2f dest); + + /** + * Apply a rotation transformation to this matrix by rotating the given amount of radians about + * the specified rotation center (x, y) and store the result in dest. + *

+ * This method is equivalent to calling: translate(x, y, dest).rotate(ang).translate(-x, -y) + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @see #translate(float, float, Matrix3x2f) + * @see #rotate(float, Matrix3x2f) + * + * @param ang + * the angle in radians + * @param x + * the x component of the rotation center + * @param y + * the y component of the rotation center + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f rotateAbout(float ang, float x, float y, Matrix3x2f dest); + + /** + * Apply a rotation transformation to this matrix that rotates the given normalized fromDir direction vector + * to point along the normalized toDir, and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the rotation will be applied first! + * + * @param fromDir + * the normalized direction which should be rotate to point along toDir + * @param toDir + * the normalized destination direction + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f rotateTo(Vector2fc fromDir, Vector2fc toDir, Matrix3x2f dest); + + /** + * Apply a "view" transformation to this matrix that maps the given (left, bottom) and + * (right, top) corners to (-1, -1) and (1, 1) respectively and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + * + * @param left + * the distance from the center to the left view edge + * @param right + * the distance from the center to the right view edge + * @param bottom + * the distance from the center to the bottom view edge + * @param top + * the distance from the center to the top view edge + * @param dest + * will hold the result + * @return dest + */ + Matrix3x2f view(float left, float right, float bottom, float top, Matrix3x2f dest); + + /** + * Obtain the position that gets transformed to the origin by this matrix. + * This can be used to get the position of the "camera" from a given view transformation matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2f inv = new Matrix3x2f(this).invertAffine();
+     * inv.transform(origin.set(0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + Vector2f origin(Vector2f origin); + + /** + * Obtain the extents of the view transformation of this matrix and store it in area. + * This can be used to determine which region of the screen (i.e. the NDC space) is covered by the view. + * + * @param area + * will hold the view area as [minX, minY, maxX, maxY] + * @return area + */ + float[] viewArea(float[] area); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 2x2 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2f inv = new Matrix3x2f(this).invert();
+     * inv.transformDirection(dir.set(1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector2f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector2f positiveX(Vector2f dir); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 2x2 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2f inv = new Matrix3x2f(this).transpose();
+     * inv.transformDirection(dir.set(1, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector2f normalizedPositiveX(Vector2f dir); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 2x2 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2f inv = new Matrix3x2f(this).invert();
+     * inv.transformDirection(dir.set(0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector2f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector2f positiveY(Vector2f dir); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 2x2 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix3x2f inv = new Matrix3x2f(this).transpose();
+     * inv.transformDirection(dir.set(0, 1));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector2f normalizedPositiveY(Vector2f dir); + + /** + * Unproject the given window coordinates (winX, winY) by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix3x2f)} and then the method {@link #unprojectInv(float, float, int[], Vector2f) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(float, float, int[], Vector2f) + * @see #invert(Matrix3x2f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector2f unproject(float winX, float winY, int[] viewport, Vector2f dest); + + /** + * Unproject the given window coordinates (winX, winY) by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(float, float, int[], Vector2f) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + * + * @see #unproject(float, float, int[], Vector2f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector2f unprojectInv(float winX, float winY, int[] viewport, Vector2f dest); + + /** + * Test whether the given point (x, y) is within the frustum defined by this matrix. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given point with the coordinates (x, y, z) given + * in space M is within the clip space. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the x-coordinate of the point + * @param y + * the y-coordinate of the point + * @return true if the given point is inside the frustum; false otherwise + */ + boolean testPoint(float x, float y); + + /** + * Test whether the given circle is partly or completely within or outside of the frustum defined by this matrix. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given sphere with the coordinates (x, y, z) given + * in space M is within the clip space. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the x-coordinate of the circle's center + * @param y + * the y-coordinate of the circle's center + * @param r + * the circle's radius + * @return true if the given circle is partly or completely inside the frustum; false otherwise + */ + boolean testCircle(float x, float y, float r); + + /** + * Test whether the given axis-aligned rectangle is partly or completely within or outside of the frustum defined by this matrix. + * The rectangle is specified via its min and max corner coordinates. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given axis-aligned rectangle with its minimum corner coordinates (minX, minY, minZ) + * and maximum corner coordinates (maxX, maxY, maxZ) given in space M is within the clip space. + *

+ * Reference: Efficient View Frustum Culling + *
+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @return true if the axis-aligned box is completely or partly inside of the frustum; false otherwise + */ + boolean testAar(float minX, float minY, float maxX, float maxY); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix3x2fc m, float delta); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4d.java new file mode 100644 index 000000000..781810144 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4d.java @@ -0,0 +1,16604 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a 4x4 Matrix of doubles, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10 m20 m30
+ * m01 m11 m21 m31
+ * m02 m12 m22 m32
+ * m03 m13 m23 m33
+ * + * @author Richard Greenlees + * @author Kai Burjack + */ +public class Matrix4d implements Externalizable, Cloneable, Matrix4dc { + + private static final long serialVersionUID = 1L; + + double m00, m01, m02, m03; + double m10, m11, m12, m13; + double m20, m21, m22, m23; + double m30, m31, m32, m33; + + int properties; + + /** + * Create a new {@link Matrix4d} and set it to {@link #identity() identity}. + */ + public Matrix4d() { + _m00(1.0). + _m11(1.0). + _m22(1.0). + _m33(1.0). + properties = PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + } + + /** + * Create a new {@link Matrix4d} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix4dc} to copy the values from + */ + public Matrix4d(Matrix4dc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4d} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix4fc} to copy the values from + */ + public Matrix4d(Matrix4fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4d} and set its upper 4x3 submatrix to the given matrix mat + * and all other elements to identity. + * + * @param mat + * the {@link Matrix4x3dc} to copy the values from + */ + public Matrix4d(Matrix4x3dc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4d} and set its upper 4x3 submatrix to the given matrix mat + * and all other elements to identity. + * + * @param mat + * the {@link Matrix4x3fc} to copy the values from + */ + public Matrix4d(Matrix4x3fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4d} by setting its uppper left 3x3 submatrix to the values of the given {@link Matrix3dc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix3dc} + */ + public Matrix4d(Matrix3dc mat) { + set(mat); + } + + /** + * Create a new 4x4 matrix using the supplied double values. + *

+ * The matrix layout will be:

+ * m00, m10, m20, m30
+ * m01, m11, m21, m31
+ * m02, m12, m22, m32
+ * m03, m13, m23, m33 + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m02 + * the value of m02 + * @param m03 + * the value of m03 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + * @param m12 + * the value of m12 + * @param m13 + * the value of m13 + * @param m20 + * the value of m20 + * @param m21 + * the value of m21 + * @param m22 + * the value of m22 + * @param m23 + * the value of m23 + * @param m30 + * the value of m30 + * @param m31 + * the value of m31 + * @param m32 + * the value of m32 + * @param m33 + * the value of m33 + */ + public Matrix4d(double m00, double m01, double m02, double m03, + double m10, double m11, double m12, double m13, + double m20, double m21, double m22, double m23, + double m30, double m31, double m32, double m33) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m03 = m03; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m13 = m13; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + this.m23 = m23; + this.m30 = m30; + this.m31 = m31; + this.m32 = m32; + this.m33 = m33; + determineProperties(); + } + + /** + * Create a new {@link Matrix4d} by reading its 16 double components from the given {@link DoubleBuffer} + * at the buffer's current position. + *

+ * That DoubleBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link DoubleBuffer} to read the matrix values from + */ + public Matrix4d(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + determineProperties(); + } + + /** + * Create a new {@link Matrix4d} and initialize its four columns using the supplied vectors. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @param col3 + * the fourth column + */ + public Matrix4d(Vector4d col0, Vector4d col1, Vector4d col2, Vector4d col3) { + set(col0, col1, col2, col3); + } + + /** + * Assume the given properties about this matrix. + *

+ * Use one or multiple of 0, {@link Matrix4dc#PROPERTY_IDENTITY}, + * {@link Matrix4dc#PROPERTY_TRANSLATION}, {@link Matrix4dc#PROPERTY_AFFINE}, + * {@link Matrix4dc#PROPERTY_PERSPECTIVE}, {@link Matrix4fc#PROPERTY_ORTHONORMAL}. + * + * @param properties + * bitset of the properties to assume about this matrix + * @return this + */ + public Matrix4d assume(int properties) { + this.properties = (byte) properties; + return this; + } + + /** + * Compute and set the matrix properties returned by {@link #properties()} based + * on the current matrix element values. + * + * @return this + */ + public Matrix4d determineProperties() { + int properties = 0; + if (m03 == 0.0 && m13 == 0.0) { + if (m23 == 0.0 && m33 == 1.0) { + properties |= PROPERTY_AFFINE; + if (m00 == 1.0 && m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m11 == 1.0 && m12 == 0.0 && m20 == 0.0 + && m21 == 0.0 && m22 == 1.0) { + properties |= PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + if (m30 == 0.0 && m31 == 0.0 && m32 == 0.0) + properties |= PROPERTY_IDENTITY; + } + /* + * We do not determine orthogonality, since it would require arbitrary epsilons + * and is rather expensive (6 dot products) in the worst case. + */ + } else if (m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m12 == 0.0 && m20 == 0.0 && m21 == 0.0 && m30 == 0.0 + && m31 == 0.0 && m33 == 0.0) { + properties |= PROPERTY_PERSPECTIVE; + } + } + this.properties = properties; + return this; + } + + public int properties() { + return properties; + } + + public double m00() { + return m00; + } + public double m01() { + return m01; + } + public double m02() { + return m02; + } + public double m03() { + return m03; + } + public double m10() { + return m10; + } + public double m11() { + return m11; + } + public double m12() { + return m12; + } + public double m13() { + return m13; + } + public double m20() { + return m20; + } + public double m21() { + return m21; + } + public double m22() { + return m22; + } + public double m23() { + return m23; + } + public double m30() { + return m30; + } + public double m31() { + return m31; + } + public double m32() { + return m32; + } + public double m33() { + return m33; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + public Matrix4d m00(double m00) { + this.m00 = m00; + properties &= ~PROPERTY_ORTHONORMAL; + if (m00 != 1.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + public Matrix4d m01(double m01) { + this.m01 = m01; + properties &= ~PROPERTY_ORTHONORMAL; + if (m01 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2. + * + * @param m02 + * the new value + * @return this + */ + public Matrix4d m02(double m02) { + this.m02 = m02; + properties &= ~PROPERTY_ORTHONORMAL; + if (m02 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 3. + * + * @param m03 + * the new value + * @return this + */ + public Matrix4d m03(double m03) { + this.m03 = m03; + if (m03 != 0.0) + properties = 0; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + public Matrix4d m10(double m10) { + this.m10 = m10; + properties &= ~PROPERTY_ORTHONORMAL; + if (m10 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + public Matrix4d m11(double m11) { + this.m11 = m11; + properties &= ~PROPERTY_ORTHONORMAL; + if (m11 != 1.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2. + * + * @param m12 + * the new value + * @return this + */ + public Matrix4d m12(double m12) { + this.m12 = m12; + properties &= ~PROPERTY_ORTHONORMAL; + if (m12 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 3. + * + * @param m13 + * the new value + * @return this + */ + public Matrix4d m13(double m13) { + this.m13 = m13; + if (m03 != 0.0) + properties = 0; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + public Matrix4d m20(double m20) { + this.m20 = m20; + properties &= ~PROPERTY_ORTHONORMAL; + if (m20 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + public Matrix4d m21(double m21) { + this.m21 = m21; + properties &= ~PROPERTY_ORTHONORMAL; + if (m21 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2. + * + * @param m22 + * the new value + * @return this + */ + public Matrix4d m22(double m22) { + this.m22 = m22; + properties &= ~PROPERTY_ORTHONORMAL; + if (m22 != 1.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 3. + * + * @param m23 + * the new value + * @return this + */ + public Matrix4d m23(double m23) { + this.m23 = m23; + if (m23 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 0. + * + * @param m30 + * the new value + * @return this + */ + public Matrix4d m30(double m30) { + this.m30 = m30; + if (m30 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 1. + * + * @param m31 + * the new value + * @return this + */ + public Matrix4d m31(double m31) { + this.m31 = m31; + if (m31 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 2. + * + * @param m32 + * the new value + * @return this + */ + public Matrix4d m32(double m32) { + this.m32 = m32; + if (m32 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 3. + * + * @param m33 + * the new value + * @return this + */ + public Matrix4d m33(double m33) { + this.m33 = m33; + if (m33 != 0.0) + properties &= ~(PROPERTY_PERSPECTIVE); + if (m33 != 1.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL | PROPERTY_AFFINE); + return this; + } + + Matrix4d _properties(int properties) { + this.properties = properties; + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0 without updating the properties of the matrix. + * + * @param m00 + * the new value + * @return this + */ + Matrix4d _m00(double m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1 without updating the properties of the matrix. + * + * @param m01 + * the new value + * @return this + */ + Matrix4d _m01(double m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2 without updating the properties of the matrix. + * + * @param m02 + * the new value + * @return this + */ + Matrix4d _m02(double m02) { + this.m02 = m02; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 3 without updating the properties of the matrix. + * + * @param m03 + * the new value + * @return this + */ + Matrix4d _m03(double m03) { + this.m03 = m03; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0 without updating the properties of the matrix. + * + * @param m10 + * the new value + * @return this + */ + Matrix4d _m10(double m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1 without updating the properties of the matrix. + * + * @param m11 + * the new value + * @return this + */ + Matrix4d _m11(double m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2 without updating the properties of the matrix. + * + * @param m12 + * the new value + * @return this + */ + Matrix4d _m12(double m12) { + this.m12 = m12; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 3 without updating the properties of the matrix. + * + * @param m13 + * the new value + * @return this + */ + Matrix4d _m13(double m13) { + this.m13 = m13; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0 without updating the properties of the matrix. + * + * @param m20 + * the new value + * @return this + */ + Matrix4d _m20(double m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1 without updating the properties of the matrix. + * + * @param m21 + * the new value + * @return this + */ + Matrix4d _m21(double m21) { + this.m21 = m21; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2 without updating the properties of the matrix. + * + * @param m22 + * the new value + * @return this + */ + Matrix4d _m22(double m22) { + this.m22 = m22; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 3 without updating the properties of the matrix. + * + * @param m23 + * the new value + * @return this + */ + Matrix4d _m23(double m23) { + this.m23 = m23; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 0 without updating the properties of the matrix. + * + * @param m30 + * the new value + * @return this + */ + Matrix4d _m30(double m30) { + this.m30 = m30; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 1 without updating the properties of the matrix. + * + * @param m31 + * the new value + * @return this + */ + Matrix4d _m31(double m31) { + this.m31 = m31; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 2 without updating the properties of the matrix. + * + * @param m32 + * the new value + * @return this + */ + Matrix4d _m32(double m32) { + this.m32 = m32; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 3 without updating the properties of the matrix. + * + * @param m33 + * the new value + * @return this + */ + Matrix4d _m33(double m33) { + this.m33 = m33; + return this; + } + + /** + * Reset this matrix to the identity. + *

+ * Please note that if a call to {@link #identity()} is immediately followed by a call to: + * {@link #translate(double, double, double) translate}, + * {@link #rotate(double, double, double, double) rotate}, + * {@link #scale(double, double, double) scale}, + * {@link #perspective(double, double, double, double) perspective}, + * {@link #frustum(double, double, double, double, double, double) frustum}, + * {@link #ortho(double, double, double, double, double, double) ortho}, + * {@link #ortho2D(double, double, double, double) ortho2D}, + * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt}, + * {@link #lookAlong(double, double, double, double, double, double) lookAlong}, + * or any of their overloads, then the call to {@link #identity()} can be omitted and the subsequent call replaced with: + * {@link #translation(double, double, double) translation}, + * {@link #rotation(double, double, double, double) rotation}, + * {@link #scaling(double, double, double) scaling}, + * {@link #setPerspective(double, double, double, double) setPerspective}, + * {@link #setFrustum(double, double, double, double, double, double) setFrustum}, + * {@link #setOrtho(double, double, double, double, double, double) setOrtho}, + * {@link #setOrtho2D(double, double, double, double) setOrtho2D}, + * {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt}, + * {@link #setLookAlong(double, double, double, double, double, double) setLookAlong}, + * or any of their overloads. + * + * @return this + */ + public Matrix4d identity() { + if ((properties & PROPERTY_IDENTITY) != 0) + return this; + _identity(); + properties = PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + return this; + } + private void _identity() { + _m00(1.0). + _m10(0.0). + _m20(0.0). + _m30(0.0). + _m01(0.0). + _m11(1.0). + _m21(0.0). + _m31(0.0). + _m02(0.0). + _m12(0.0). + _m22(1.0). + _m32(0.0). + _m03(0.0). + _m13(0.0). + _m23(0.0). + _m33(1.0); + } + + /** + * Store the values of the given matrix m into this matrix. + * + * @see #Matrix4d(Matrix4dc) + * @see #get(Matrix4d) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4d set(Matrix4dc m) { + return + _m00(m.m00()). + _m01(m.m01()). + _m02(m.m02()). + _m03(m.m03()). + _m10(m.m10()). + _m11(m.m11()). + _m12(m.m12()). + _m13(m.m13()). + _m20(m.m20()). + _m21(m.m21()). + _m22(m.m22()). + _m23(m.m23()). + _m30(m.m30()). + _m31(m.m31()). + _m32(m.m32()). + _m33(m.m33()). + _properties(m.properties()); + } + + /** + * Store the values of the given matrix m into this matrix. + * + * @see #Matrix4d(Matrix4fc) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4d set(Matrix4fc m) { + return + _m00(m.m00()). + _m01(m.m01()). + _m02(m.m02()). + _m03(m.m03()). + _m10(m.m10()). + _m11(m.m11()). + _m12(m.m12()). + _m13(m.m13()). + _m20(m.m20()). + _m21(m.m21()). + _m22(m.m22()). + _m23(m.m23()). + _m30(m.m30()). + _m31(m.m31()). + _m32(m.m32()). + _m33(m.m33()). + _properties(m.properties()); + } + + /** + * Store the values of the transpose of the given matrix m into this matrix. + * + * @param m + * the matrix to copy the transposed values from + * @return this + */ + public Matrix4d setTransposed(Matrix4dc m) { + if ((m.properties() & PROPERTY_IDENTITY) != 0) + return this.identity(); + return setTransposedInternal(m); + } + private Matrix4d setTransposedInternal(Matrix4dc m) { + double nm10 = m.m01(), nm12 = m.m21(), nm13 = m.m31(); + double nm20 = m.m02(), nm21 = m.m12(), nm30 = m.m03(); + double nm31 = m.m13(), nm32 = m.m23(); + return this + ._m00(m.m00())._m01(m.m10())._m02(m.m20())._m03(m.m30()) + ._m10(nm10)._m11(m.m11())._m12(nm12)._m13(nm13) + ._m20(nm20)._m21(nm21)._m22(m.m22())._m23(m.m32()) + ._m30(nm30)._m31(nm31)._m32(nm32)._m33(m.m33()) + ._properties(m.properties() & PROPERTY_IDENTITY); + } + + /** + * Store the values of the given matrix m into this matrix + * and set the other matrix elements to identity. + * + * @see #Matrix4d(Matrix4x3dc) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4d set(Matrix4x3dc m) { + return + _m00(m.m00()). + _m01(m.m01()). + _m02(m.m02()). + _m03(0.0). + _m10(m.m10()). + _m11(m.m11()). + _m12(m.m12()). + _m13(0.0). + _m20(m.m20()). + _m21(m.m21()). + _m22(m.m22()). + _m23(0.0). + _m30(m.m30()). + _m31(m.m31()). + _m32(m.m32()). + _m33(1.0). + _properties(m.properties() | PROPERTY_AFFINE); + } + + /** + * Store the values of the given matrix m into this matrix + * and set the other matrix elements to identity. + * + * @see #Matrix4d(Matrix4x3fc) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4d set(Matrix4x3fc m) { + return + _m00(m.m00()). + _m01(m.m01()). + _m02(m.m02()). + _m03(0.0). + _m10(m.m10()). + _m11(m.m11()). + _m12(m.m12()). + _m13(0.0). + _m20(m.m20()). + _m21(m.m21()). + _m22(m.m22()). + _m23(0.0). + _m30(m.m30()). + _m31(m.m31()). + _m32(m.m32()). + _m33(1.0). + _properties(m.properties() | PROPERTY_AFFINE); + } + + /** + * Set the upper left 3x3 submatrix of this {@link Matrix4d} to the given {@link Matrix3dc} + * and the rest to identity. + * + * @see #Matrix4d(Matrix3dc) + * + * @param mat + * the {@link Matrix3dc} + * @return this + */ + public Matrix4d set(Matrix3dc mat) { + return + _m00(mat.m00()). + _m01(mat.m01()). + _m02(mat.m02()). + _m03(0.0). + _m10(mat.m10()). + _m11(mat.m11()). + _m12(mat.m12()). + _m13(0.0). + _m20(mat.m20()). + _m21(mat.m21()). + _m22(mat.m22()). + _m23(0.0). + _m30(0.0). + _m31(0.0). + _m32(0.0). + _m33(1.0). + _properties(PROPERTY_AFFINE); + } + + /** + * Set the upper left 3x3 submatrix of this {@link Matrix4d} to that of the given {@link Matrix4dc} + * and don't change the other elements. + * + * @param mat + * the {@link Matrix4dc} + * @return this + */ + public Matrix4d set3x3(Matrix4dc mat) { + return + _m00(mat.m00()). + _m01(mat.m01()). + _m02(mat.m02()). + _m10(mat.m10()). + _m11(mat.m11()). + _m12(mat.m12()). + _m20(mat.m20()). + _m21(mat.m21()). + _m22(mat.m22()). + _properties(properties & mat.properties() & ~(PROPERTY_PERSPECTIVE)); + } + + /** + * Set the upper 4x3 submatrix of this {@link Matrix4d} to the given {@link Matrix4x3dc} + * and don't change the other elements. + * + * @see Matrix4x3dc#get(Matrix4d) + * + * @param mat + * the {@link Matrix4x3dc} + * @return this + */ + public Matrix4d set4x3(Matrix4x3dc mat) { + return + _m00(mat.m00()). + _m01(mat.m01()). + _m02(mat.m02()). + _m10(mat.m10()). + _m11(mat.m11()). + _m12(mat.m12()). + _m20(mat.m20()). + _m21(mat.m21()). + _m22(mat.m22()). + _m30(mat.m30()). + _m31(mat.m31()). + _m32(mat.m32()). + _properties(properties & mat.properties() & ~(PROPERTY_PERSPECTIVE)); + } + + /** + * Set the upper 4x3 submatrix of this {@link Matrix4d} to the given {@link Matrix4x3fc} + * and don't change the other elements. + * + * @see Matrix4x3fc#get(Matrix4d) + * + * @param mat + * the {@link Matrix4x3fc} + * @return this + */ + public Matrix4d set4x3(Matrix4x3fc mat) { + return + _m00(mat.m00()). + _m01(mat.m01()). + _m02(mat.m02()). + _m10(mat.m10()). + _m11(mat.m11()). + _m12(mat.m12()). + _m20(mat.m20()). + _m21(mat.m21()). + _m22(mat.m22()). + _m30(mat.m30()). + _m31(mat.m31()). + _m32(mat.m32()). + _properties(properties & mat.properties() & ~(PROPERTY_PERSPECTIVE)); + } + + /** + * Set the upper 4x3 submatrix of this {@link Matrix4d} to the upper 4x3 submatrix of the given {@link Matrix4dc} + * and don't change the other elements. + * + * @param mat + * the {@link Matrix4dc} + * @return this + */ + public Matrix4d set4x3(Matrix4dc mat) { + return + _m00(mat.m00()). + _m01(mat.m01()). + _m02(mat.m02()). + _m10(mat.m10()). + _m11(mat.m11()). + _m12(mat.m12()). + _m20(mat.m20()). + _m21(mat.m21()). + _m22(mat.m22()). + _m30(mat.m30()). + _m31(mat.m31()). + _m32(mat.m32()). + _properties(properties & mat.properties() & ~(PROPERTY_PERSPECTIVE)); + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + * @return this + */ + public Matrix4d set(AxisAngle4f axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double invLength = Math.invsqrt(x*x + y*y + z*z); + x *= invLength; + y *= invLength; + z *= invLength; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0 - c; + _m00(c + x*x*omc). + _m11(c + y*y*omc). + _m22(c + z*z*omc); + double tmp1 = x*y*omc; + double tmp2 = z*s; + _m10(tmp1 - tmp2). + _m01(tmp1 + tmp2); + tmp1 = x*z*omc; + tmp2 = y*s; + _m20(tmp1 + tmp2). + _m02(tmp1 - tmp2); + tmp1 = y*z*omc; + tmp2 = x*s; + _m21(tmp1 - tmp2). + _m12(tmp1 + tmp2). + _m03(0.0). + _m13(0.0). + _m23(0.0). + _m30(0.0). + _m31(0.0). + _m32(0.0). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + * @return this + */ + public Matrix4d set(AxisAngle4d axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double invLength = Math.invsqrt(x*x + y*y + z*z); + x *= invLength; + y *= invLength; + z *= invLength; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0 - c; + _m00(c + x*x*omc). + _m11(c + y*y*omc). + _m22(c + z*z*omc); + double tmp1 = x*y*omc; + double tmp2 = z*s; + _m10(tmp1 - tmp2). + _m01(tmp1 + tmp2); + tmp1 = x*z*omc; + tmp2 = y*s; + _m20(tmp1 + tmp2). + _m02(tmp1 - tmp2); + tmp1 = y*z*omc; + tmp2 = x*s; + _m21(tmp1 - tmp2). + _m12(tmp1 + tmp2). + _m03(0.0). + _m13(0.0). + _m23(0.0). + _m30(0.0). + _m31(0.0). + _m32(0.0). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaternionfc}. + *

+ * This method is equivalent to calling: rotation(q) + *

+ * Reference: http://www.euclideanspace.com/ + * + * @see #rotation(Quaternionfc) + * + * @param q + * the {@link Quaternionfc} + * @return this + */ + public Matrix4d set(Quaternionfc q) { + return rotation(q); + } + + /** + * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaterniondc}. + *

+ * This method is equivalent to calling: rotation(q) + *

+ * Reference: http://www.euclideanspace.com/ + * + * @see #rotation(Quaterniondc) + * + * @param q + * the {@link Quaterniondc} + * @return this + */ + public Matrix4d set(Quaterniondc q) { + return rotation(q); + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the multiplication + * @return this + */ + public Matrix4d mul(Matrix4dc right) { + return mul(right, this); + } + + public Matrix4d mul(Matrix4dc right, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_TRANSLATION) != 0 && (right.properties() & PROPERTY_AFFINE) != 0) + return mulTranslationAffine(right, dest); + else if ((properties & PROPERTY_AFFINE) != 0 && (right.properties() & PROPERTY_AFFINE) != 0) + return mulAffine(right, dest); + else if ((properties & PROPERTY_PERSPECTIVE) != 0 && (right.properties() & PROPERTY_AFFINE) != 0) + return mulPerspectiveAffine(right, dest); + else if ((right.properties() & PROPERTY_AFFINE) != 0) + return mulAffineR(right, dest); + return mul0(right, dest); + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + *

+ * This method neither assumes nor checks for any matrix properties of this or right + * and will always perform a complete 4x4 matrix multiplication. This method should only be used whenever the + * multiplied matrices do not have any properties for which there are optimized multiplication methods available. + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4d mul0(Matrix4dc right) { + return mul0(right, this); + } + + public Matrix4d mul0(Matrix4dc right, Matrix4d dest) { + double nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), Math.fma(m20, right.m02(), m30 * right.m03()))); + double nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), Math.fma(m21, right.m02(), m31 * right.m03()))); + double nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), Math.fma(m22, right.m02(), m32 * right.m03()))); + double nm03 = Math.fma(m03, right.m00(), Math.fma(m13, right.m01(), Math.fma(m23, right.m02(), m33 * right.m03()))); + double nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), Math.fma(m20, right.m12(), m30 * right.m13()))); + double nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), Math.fma(m21, right.m12(), m31 * right.m13()))); + double nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), Math.fma(m22, right.m12(), m32 * right.m13()))); + double nm13 = Math.fma(m03, right.m10(), Math.fma(m13, right.m11(), Math.fma(m23, right.m12(), m33 * right.m13()))); + double nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), Math.fma(m20, right.m22(), m30 * right.m23()))); + double nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), Math.fma(m21, right.m22(), m31 * right.m23()))); + double nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), Math.fma(m22, right.m22(), m32 * right.m23()))); + double nm23 = Math.fma(m03, right.m20(), Math.fma(m13, right.m21(), Math.fma(m23, right.m22(), m33 * right.m23()))); + double nm30 = Math.fma(m00, right.m30(), Math.fma(m10, right.m31(), Math.fma(m20, right.m32(), m30 * right.m33()))); + double nm31 = Math.fma(m01, right.m30(), Math.fma(m11, right.m31(), Math.fma(m21, right.m32(), m31 * right.m33()))); + double nm32 = Math.fma(m02, right.m30(), Math.fma(m12, right.m31(), Math.fma(m22, right.m32(), m32 * right.m33()))); + double nm33 = Math.fma(m03, right.m30(), Math.fma(m13, right.m31(), Math.fma(m23, right.m32(), m33 * right.m33()))); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Multiply this matrix by the matrix with the supplied elements. + *

+ * If M is this matrix and R the right matrix whose + * elements are supplied via the parameters, then the new matrix will be M * R. + * So when transforming a vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param r00 + * the m00 element of the right matrix + * @param r01 + * the m01 element of the right matrix + * @param r02 + * the m02 element of the right matrix + * @param r03 + * the m03 element of the right matrix + * @param r10 + * the m10 element of the right matrix + * @param r11 + * the m11 element of the right matrix + * @param r12 + * the m12 element of the right matrix + * @param r13 + * the m13 element of the right matrix + * @param r20 + * the m20 element of the right matrix + * @param r21 + * the m21 element of the right matrix + * @param r22 + * the m22 element of the right matrix + * @param r23 + * the m23 element of the right matrix + * @param r30 + * the m30 element of the right matrix + * @param r31 + * the m31 element of the right matrix + * @param r32 + * the m32 element of the right matrix + * @param r33 + * the m33 element of the right matrix + * @return this + */ + public Matrix4d mul( + double r00, double r01, double r02, double r03, + double r10, double r11, double r12, double r13, + double r20, double r21, double r22, double r23, + double r30, double r31, double r32, double r33) { + return mul(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, this); + } + + public Matrix4d mul( + double r00, double r01, double r02, double r03, + double r10, double r11, double r12, double r13, + double r20, double r21, double r22, double r23, + double r30, double r31, double r32, double r33, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33); + else if ((properties & PROPERTY_AFFINE) != 0) + return mulAffineL(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest); + return mulGeneric(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest); + } + private Matrix4d mulAffineL( + double r00, double r01, double r02, double r03, + double r10, double r11, double r12, double r13, + double r20, double r21, double r22, double r23, + double r30, double r31, double r32, double r33, Matrix4d dest) { + double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03))); + double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03))); + double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03))); + double nm03 = r03; + double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13))); + double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13))); + double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13))); + double nm13 = r13; + double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23))); + double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23))); + double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23))); + double nm23 = r23; + double nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33))); + double nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33))); + double nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33))); + double nm33 = r33; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(PROPERTY_AFFINE); + } + private Matrix4d mulGeneric( + double r00, double r01, double r02, double r03, + double r10, double r11, double r12, double r13, + double r20, double r21, double r22, double r23, + double r30, double r31, double r32, double r33, Matrix4d dest) { + double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03))); + double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03))); + double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03))); + double nm03 = Math.fma(m03, r00, Math.fma(m13, r01, Math.fma(m23, r02, m33 * r03))); + double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13))); + double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13))); + double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13))); + double nm13 = Math.fma(m03, r10, Math.fma(m13, r11, Math.fma(m23, r12, m33 * r13))); + double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23))); + double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23))); + double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23))); + double nm23 = Math.fma(m03, r20, Math.fma(m13, r21, Math.fma(m23, r22, m33 * r23))); + double nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33))); + double nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33))); + double nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33))); + double nm33 = Math.fma(m03, r30, Math.fma(m13, r31, Math.fma(m23, r32, m33 * r33))); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Multiply this matrix by the 3x3 matrix with the supplied elements expanded to a 4x4 matrix with + * all other matrix elements set to identity. + *

+ * If M is this matrix and R the right matrix whose + * elements are supplied via the parameters, then the new matrix will be M * R. + * So when transforming a vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param r00 + * the m00 element of the right matrix + * @param r01 + * the m01 element of the right matrix + * @param r02 + * the m02 element of the right matrix + * @param r10 + * the m10 element of the right matrix + * @param r11 + * the m11 element of the right matrix + * @param r12 + * the m12 element of the right matrix + * @param r20 + * the m20 element of the right matrix + * @param r21 + * the m21 element of the right matrix + * @param r22 + * the m22 element of the right matrix + * @return this + */ + public Matrix4d mul3x3( + double r00, double r01, double r02, + double r10, double r11, double r12, + double r20, double r21, double r22) { + return mul3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, this); + } + public Matrix4d mul3x3( + double r00, double r01, double r02, + double r10, double r11, double r12, + double r20, double r21, double r22, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(r00, r01, r02, 0, r10, r11, r12, 0, r20, r21, r22, 0, 0, 0, 0, 1); + return mulGeneric3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, dest); + } + private Matrix4d mulGeneric3x3( + double r00, double r01, double r02, + double r10, double r11, double r12, + double r20, double r21, double r22, Matrix4d dest) { + double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, m20 * r02)); + double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, m21 * r02)); + double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, m22 * r02)); + double nm03 = Math.fma(m03, r00, Math.fma(m13, r01, m23 * r02)); + double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, m20 * r12)); + double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, m21 * r12)); + double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, m22 * r12)); + double nm13 = Math.fma(m03, r10, Math.fma(m13, r11, m23 * r12)); + double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, m20 * r22)); + double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, m21 * r22)); + double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, m22 * r22)); + double nm23 = Math.fma(m03, r20, Math.fma(m13, r21, m23 * r22)); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(this.properties & PROPERTY_AFFINE); + } + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in this. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @return this + */ + public Matrix4d mulLocal(Matrix4dc left) { + return mulLocal(left, this); + } + + public Matrix4d mulLocal(Matrix4dc left, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(left); + else if ((left.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_AFFINE) != 0 && (left.properties() & PROPERTY_AFFINE) != 0) + return mulLocalAffine(left, dest); + return mulLocalGeneric(left, dest); + } + private Matrix4d mulLocalGeneric(Matrix4dc left, Matrix4d dest) { + double nm00 = Math.fma(left.m00(), m00, Math.fma(left.m10(), m01, Math.fma(left.m20(), m02, left.m30() * m03))); + double nm01 = Math.fma(left.m01(), m00, Math.fma(left.m11(), m01, Math.fma(left.m21(), m02, left.m31() * m03))); + double nm02 = Math.fma(left.m02(), m00, Math.fma(left.m12(), m01, Math.fma(left.m22(), m02, left.m32() * m03))); + double nm03 = Math.fma(left.m03(), m00, Math.fma(left.m13(), m01, Math.fma(left.m23(), m02, left.m33() * m03))); + double nm10 = Math.fma(left.m00(), m10, Math.fma(left.m10(), m11, Math.fma(left.m20(), m12, left.m30() * m13))); + double nm11 = Math.fma(left.m01(), m10, Math.fma(left.m11(), m11, Math.fma(left.m21(), m12, left.m31() * m13))); + double nm12 = Math.fma(left.m02(), m10, Math.fma(left.m12(), m11, Math.fma(left.m22(), m12, left.m32() * m13))); + double nm13 = Math.fma(left.m03(), m10, Math.fma(left.m13(), m11, Math.fma(left.m23(), m12, left.m33() * m13))); + double nm20 = Math.fma(left.m00(), m20, Math.fma(left.m10(), m21, Math.fma(left.m20(), m22, left.m30() * m23))); + double nm21 = Math.fma(left.m01(), m20, Math.fma(left.m11(), m21, Math.fma(left.m21(), m22, left.m31() * m23))); + double nm22 = Math.fma(left.m02(), m20, Math.fma(left.m12(), m21, Math.fma(left.m22(), m22, left.m32() * m23))); + double nm23 = Math.fma(left.m03(), m20, Math.fma(left.m13(), m21, Math.fma(left.m23(), m22, left.m33() * m23))); + double nm30 = Math.fma(left.m00(), m30, Math.fma(left.m10(), m31, Math.fma(left.m20(), m32, left.m30() * m33))); + double nm31 = Math.fma(left.m01(), m30, Math.fma(left.m11(), m31, Math.fma(left.m21(), m32, left.m31() * m33))); + double nm32 = Math.fma(left.m02(), m30, Math.fma(left.m12(), m31, Math.fma(left.m22(), m32, left.m32() * m33))); + double nm33 = Math.fma(left.m03(), m30, Math.fma(left.m13(), m31, Math.fma(left.m23(), m32, left.m33() * m33))); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Pre-multiply this matrix by the supplied left matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in this. + *

+ * This method assumes that this matrix and the given left matrix both represent an {@link #isAffine() affine} transformation + * (i.e. their last rows are equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * This method will not modify either the last row of this or the last row of left. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @return this + */ + public Matrix4d mulLocalAffine(Matrix4dc left) { + return mulLocalAffine(left, this); + } + + public Matrix4d mulLocalAffine(Matrix4dc left, Matrix4d dest) { + double nm00 = left.m00() * m00 + left.m10() * m01 + left.m20() * m02; + double nm01 = left.m01() * m00 + left.m11() * m01 + left.m21() * m02; + double nm02 = left.m02() * m00 + left.m12() * m01 + left.m22() * m02; + double nm03 = left.m03(); + double nm10 = left.m00() * m10 + left.m10() * m11 + left.m20() * m12; + double nm11 = left.m01() * m10 + left.m11() * m11 + left.m21() * m12; + double nm12 = left.m02() * m10 + left.m12() * m11 + left.m22() * m12; + double nm13 = left.m13(); + double nm20 = left.m00() * m20 + left.m10() * m21 + left.m20() * m22; + double nm21 = left.m01() * m20 + left.m11() * m21 + left.m21() * m22; + double nm22 = left.m02() * m20 + left.m12() * m21 + left.m22() * m22; + double nm23 = left.m23(); + double nm30 = left.m00() * m30 + left.m10() * m31 + left.m20() * m32 + left.m30(); + double nm31 = left.m01() * m30 + left.m11() * m31 + left.m21() * m32 + left.m31(); + double nm32 = left.m02() * m30 + left.m12() * m31 + left.m22() * m32 + left.m32(); + double nm33 = left.m33(); + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(PROPERTY_AFFINE); + return dest; + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * The last row of the right matrix is assumed to be (0, 0, 0, 1). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4d mul(Matrix4x3dc right) { + return mul(right, this); + } + + public Matrix4d mul(Matrix4x3dc right, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return mulTranslation(right, dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return mulAffine(right, dest); + else if ((properties & PROPERTY_PERSPECTIVE) != 0) + return mulPerspectiveAffine(right, dest); + return mulGeneric(right, dest); + } + private Matrix4d mulTranslation(Matrix4x3dc right, Matrix4d dest) { + return dest + ._m00(right.m00()) + ._m01(right.m01()) + ._m02(right.m02()) + ._m03(m03) + ._m10(right.m10()) + ._m11(right.m11()) + ._m12(right.m12()) + ._m13(m13) + ._m20(right.m20()) + ._m21(right.m21()) + ._m22(right.m22()) + ._m23(m23) + ._m30(right.m30() + m30) + ._m31(right.m31() + m31) + ._m32(right.m32() + m32) + ._m33(m33) + ._properties(PROPERTY_AFFINE | (right.properties() & PROPERTY_ORTHONORMAL)); + } + private Matrix4d mulAffine(Matrix4x3dc right, Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + double m20 = this.m20, m21 = this.m21, m22 = this.m22; + double rm00 = right.m00(), rm01 = right.m01(), rm02 = right.m02(); + double rm10 = right.m10(), rm11 = right.m11(), rm12 = right.m12(); + double rm20 = right.m20(), rm21 = right.m21(), rm22 = right.m22(); + double rm30 = right.m30(), rm31 = right.m31(), rm32 = right.m32(); + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m03(m03) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m13(m13) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m23(m23) + ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30)))) + ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31)))) + ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32)))) + ._m33(m33) + ._properties(PROPERTY_AFFINE | (this.properties & right.properties() & PROPERTY_ORTHONORMAL)); + } + private Matrix4d mulGeneric(Matrix4x3dc right, Matrix4d dest) { + double nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), m20 * right.m02())); + double nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), m21 * right.m02())); + double nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), m22 * right.m02())); + double nm03 = Math.fma(m03, right.m00(), Math.fma(m13, right.m01(), m23 * right.m02())); + double nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), m20 * right.m12())); + double nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), m21 * right.m12())); + double nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), m22 * right.m12())); + double nm13 = Math.fma(m03, right.m10(), Math.fma(m13, right.m11(), m23 * right.m12())); + double nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), m20 * right.m22())); + double nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), m21 * right.m22())); + double nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), m22 * right.m22())); + double nm23 = Math.fma(m03, right.m20(), Math.fma(m13, right.m21(), m23 * right.m22())); + double nm30 = Math.fma(m00, right.m30(), Math.fma(m10, right.m31(), Math.fma(m20, right.m32(), m30))); + double nm31 = Math.fma(m01, right.m30(), Math.fma(m11, right.m31(), Math.fma(m21, right.m32(), m31))); + double nm32 = Math.fma(m02, right.m30(), Math.fma(m12, right.m31(), Math.fma(m22, right.m32(), m32))); + double nm33 = Math.fma(m03, right.m30(), Math.fma(m13, right.m31(), Math.fma(m23, right.m32(), m33))); + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + public Matrix4d mulPerspectiveAffine(Matrix4x3dc view, Matrix4d dest) { + double lm00 = m00, lm11 = m11, lm22 = m22, lm23 = m23; + dest._m00(lm00 * view.m00())._m01(lm11 * view.m01())._m02(lm22 * view.m02())._m03(lm23 * view.m02()). + _m10(lm00 * view.m10())._m11(lm11 * view.m11())._m12(lm22 * view.m12())._m13(lm23 * view.m12()). + _m20(lm00 * view.m20())._m21(lm11 * view.m21())._m22(lm22 * view.m22())._m23(lm23 * view.m22()). + _m30(lm00 * view.m30())._m31(lm11 * view.m31())._m32(lm22 * view.m32() + m32)._m33(lm23 * view.m32()) + ._properties(0); + return dest; + } + + public Matrix4d mul(Matrix4x3fc right, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + return mulGeneric(right, dest); + } + private Matrix4d mulGeneric(Matrix4x3fc right, Matrix4d dest) { + double nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), m20 * right.m02())); + double nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), m21 * right.m02())); + double nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), m22 * right.m02())); + double nm03 = Math.fma(m03, right.m00(), Math.fma(m13, right.m01(), m23 * right.m02())); + double nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), m20 * right.m12())); + double nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), m21 * right.m12())); + double nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), m22 * right.m12())); + double nm13 = Math.fma(m03, right.m10(), Math.fma(m13, right.m11(), m23 * right.m12())); + double nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), m20 * right.m22())); + double nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), m21 * right.m22())); + double nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), m22 * right.m22())); + double nm23 = Math.fma(m03, right.m20(), Math.fma(m13, right.m21(), m23 * right.m22())); + double nm30 = Math.fma(m00, right.m30(), Math.fma(m10, right.m31(), Math.fma(m20, right.m32(), m30))); + double nm31 = Math.fma(m01, right.m30(), Math.fma(m11, right.m31(), Math.fma(m21, right.m32(), m31))); + double nm32 = Math.fma(m02, right.m30(), Math.fma(m12, right.m31(), Math.fma(m22, right.m32(), m32))); + double nm33 = Math.fma(m03, right.m30(), Math.fma(m13, right.m31(), Math.fma(m23, right.m32(), m33))); + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Multiply this matrix by the supplied right matrix and store the result in this. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4d mul(Matrix3x2dc right) { + return mul(right, this); + } + + public Matrix4d mul(Matrix3x2dc right, Matrix4d dest) { + double nm00 = m00 * right.m00() + m10 * right.m01(); + double nm01 = m01 * right.m00() + m11 * right.m01(); + double nm02 = m02 * right.m00() + m12 * right.m01(); + double nm03 = m03 * right.m00() + m13 * right.m01(); + double nm10 = m00 * right.m10() + m10 * right.m11(); + double nm11 = m01 * right.m10() + m11 * right.m11(); + double nm12 = m02 * right.m10() + m12 * right.m11(); + double nm13 = m03 * right.m10() + m13 * right.m11(); + double nm30 = m00 * right.m20() + m10 * right.m21() + m30; + double nm31 = m01 * right.m20() + m11 * right.m21() + m31; + double nm32 = m02 * right.m20() + m12 * right.m21() + m32; + double nm33 = m03 * right.m20() + m13 * right.m21() + m33; + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Multiply this matrix by the supplied right matrix and store the result in this. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4d mul(Matrix3x2fc right) { + return mul(right, this); + } + + public Matrix4d mul(Matrix3x2fc right, Matrix4d dest) { + double nm00 = m00 * right.m00() + m10 * right.m01(); + double nm01 = m01 * right.m00() + m11 * right.m01(); + double nm02 = m02 * right.m00() + m12 * right.m01(); + double nm03 = m03 * right.m00() + m13 * right.m01(); + double nm10 = m00 * right.m10() + m10 * right.m11(); + double nm11 = m01 * right.m10() + m11 * right.m11(); + double nm12 = m02 * right.m10() + m12 * right.m11(); + double nm13 = m03 * right.m10() + m13 * right.m11(); + double nm30 = m00 * right.m20() + m10 * right.m21() + m30; + double nm31 = m01 * right.m20() + m11 * right.m21() + m31; + double nm32 = m02 * right.m20() + m12 * right.m21() + m32; + double nm33 = m03 * right.m20() + m13 * right.m21() + m33; + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Multiply this matrix by the supplied parameter matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the multiplication + * @return this + */ + public Matrix4d mul(Matrix4f right) { + return mul(right, this); + } + + public Matrix4d mul(Matrix4fc right, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + return mulGeneric(right, dest); + } + private Matrix4d mulGeneric(Matrix4fc right, Matrix4d dest) { + double nm00 = m00 * right.m00() + m10 * right.m01() + m20 * right.m02() + m30 * right.m03(); + double nm01 = m01 * right.m00() + m11 * right.m01() + m21 * right.m02() + m31 * right.m03(); + double nm02 = m02 * right.m00() + m12 * right.m01() + m22 * right.m02() + m32 * right.m03(); + double nm03 = m03 * right.m00() + m13 * right.m01() + m23 * right.m02() + m33 * right.m03(); + double nm10 = m00 * right.m10() + m10 * right.m11() + m20 * right.m12() + m30 * right.m13(); + double nm11 = m01 * right.m10() + m11 * right.m11() + m21 * right.m12() + m31 * right.m13(); + double nm12 = m02 * right.m10() + m12 * right.m11() + m22 * right.m12() + m32 * right.m13(); + double nm13 = m03 * right.m10() + m13 * right.m11() + m23 * right.m12() + m33 * right.m13(); + double nm20 = m00 * right.m20() + m10 * right.m21() + m20 * right.m22() + m30 * right.m23(); + double nm21 = m01 * right.m20() + m11 * right.m21() + m21 * right.m22() + m31 * right.m23(); + double nm22 = m02 * right.m20() + m12 * right.m21() + m22 * right.m22() + m32 * right.m23(); + double nm23 = m03 * right.m20() + m13 * right.m21() + m23 * right.m22() + m33 * right.m23(); + double nm30 = m00 * right.m30() + m10 * right.m31() + m20 * right.m32() + m30 * right.m33(); + double nm31 = m01 * right.m30() + m11 * right.m31() + m21 * right.m32() + m31 * right.m33(); + double nm32 = m02 * right.m30() + m12 * right.m31() + m22 * right.m32() + m32 * right.m33(); + double nm33 = m03 * right.m30() + m13 * right.m31() + m23 * right.m32() + m33 * right.m33(); + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + return dest; + } + + /** + * Multiply this symmetric perspective projection matrix by the supplied {@link #isAffine() affine} view matrix. + *

+ * If P is this matrix and V the view matrix, + * then the new matrix will be P * V. So when transforming a + * vector v with the new matrix by using P * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the {@link #isAffine() affine} matrix to multiply this symmetric perspective projection matrix by + * @return this + */ + public Matrix4d mulPerspectiveAffine(Matrix4dc view) { + return mulPerspectiveAffine(view, this); + } + + public Matrix4d mulPerspectiveAffine(Matrix4dc view, Matrix4d dest) { + double nm00 = m00 * view.m00(), nm01 = m11 * view.m01(), nm02 = m22 * view.m02(), nm03 = m23 * view.m02(); + double nm10 = m00 * view.m10(), nm11 = m11 * view.m11(), nm12 = m22 * view.m12(), nm13 = m23 * view.m12(); + double nm20 = m00 * view.m20(), nm21 = m11 * view.m21(), nm22 = m22 * view.m22(), nm23 = m23 * view.m22(); + double nm30 = m00 * view.m30(), nm31 = m11 * view.m31(), nm32 = m22 * view.m32() + m32, nm33 = m23 * view.m32(); + return dest + ._m00(nm00)._m01(nm01)._m02(nm02)._m03(nm03) + ._m10(nm10)._m11(nm11)._m12(nm12)._m13(nm13) + ._m20(nm20)._m21(nm21)._m22(nm22)._m23(nm23) + ._m30(nm30)._m31(nm31)._m32(nm32)._m33(nm33) + ._properties(0); + } + + /** + * Multiply this matrix by the supplied right matrix, which is assumed to be {@link #isAffine() affine}, and store the result in this. + *

+ * This method assumes that the given right matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @return this + */ + public Matrix4d mulAffineR(Matrix4dc right) { + return mulAffineR(right, this); + } + + public Matrix4d mulAffineR(Matrix4dc right, Matrix4d dest) { + double nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), m20 * right.m02())); + double nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), m21 * right.m02())); + double nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), m22 * right.m02())); + double nm03 = Math.fma(m03, right.m00(), Math.fma(m13, right.m01(), m23 * right.m02())); + double nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), m20 * right.m12())); + double nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), m21 * right.m12())); + double nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), m22 * right.m12())); + double nm13 = Math.fma(m03, right.m10(), Math.fma(m13, right.m11(), m23 * right.m12())); + double nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), m20 * right.m22())); + double nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), m21 * right.m22())); + double nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), m22 * right.m22())); + double nm23 = Math.fma(m03, right.m20(), Math.fma(m13, right.m21(), m23 * right.m22())); + double nm30 = Math.fma(m00, right.m30(), Math.fma(m10, right.m31(), Math.fma(m20, right.m32(), m30))); + double nm31 = Math.fma(m01, right.m30(), Math.fma(m11, right.m31(), Math.fma(m21, right.m32(), m31))); + double nm32 = Math.fma(m02, right.m30(), Math.fma(m12, right.m31(), Math.fma(m22, right.m32(), m32))); + double nm33 = Math.fma(m03, right.m30(), Math.fma(m13, right.m31(), Math.fma(m23, right.m32(), m33))); + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Multiply this matrix by the supplied right matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in this. + *

+ * This method assumes that this matrix and the given right matrix both represent an {@link #isAffine() affine} transformation + * (i.e. their last rows are equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @return this + */ + public Matrix4d mulAffine(Matrix4dc right) { + return mulAffine(right, this); + } + + public Matrix4d mulAffine(Matrix4dc right, Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + double m20 = this.m20, m21 = this.m21, m22 = this.m22; + double rm00 = right.m00(), rm01 = right.m01(), rm02 = right.m02(); + double rm10 = right.m10(), rm11 = right.m11(), rm12 = right.m12(); + double rm20 = right.m20(), rm21 = right.m21(), rm22 = right.m22(); + double rm30 = right.m30(), rm31 = right.m31(), rm32 = right.m32(); + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m03(m03) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m13(m13) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m23(m23) + ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30)))) + ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31)))) + ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32)))) + ._m33(m33) + ._properties(PROPERTY_AFFINE | (this.properties & right.properties() & PROPERTY_ORTHONORMAL)); + } + + public Matrix4d mulTranslationAffine(Matrix4dc right, Matrix4d dest) { + return dest + ._m00(right.m00()) + ._m01(right.m01()) + ._m02(right.m02()) + ._m03(m03) + ._m10(right.m10()) + ._m11(right.m11()) + ._m12(right.m12()) + ._m13(m13) + ._m20(right.m20()) + ._m21(right.m21()) + ._m22(right.m22()) + ._m23(m23) + ._m30(right.m30() + m30) + ._m31(right.m31() + m31) + ._m32(right.m32() + m32) + ._m33(m33) + ._properties(PROPERTY_AFFINE | (right.properties() & PROPERTY_ORTHONORMAL)); + } + + /** + * Multiply this orthographic projection matrix by the supplied {@link #isAffine() affine} view matrix. + *

+ * If M is this matrix and V the view matrix, + * then the new matrix will be M * V. So when transforming a + * vector v with the new matrix by using M * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the affine matrix which to multiply this with + * @return this + */ + public Matrix4d mulOrthoAffine(Matrix4dc view) { + return mulOrthoAffine(view, this); + } + + public Matrix4d mulOrthoAffine(Matrix4dc view, Matrix4d dest) { + double nm00 = m00 * view.m00(); + double nm01 = m11 * view.m01(); + double nm02 = m22 * view.m02(); + double nm03 = 0.0; + double nm10 = m00 * view.m10(); + double nm11 = m11 * view.m11(); + double nm12 = m22 * view.m12(); + double nm13 = 0.0; + double nm20 = m00 * view.m20(); + double nm21 = m11 * view.m21(); + double nm22 = m22 * view.m22(); + double nm23 = 0.0; + double nm30 = m00 * view.m30() + m30; + double nm31 = m11 * view.m31() + m31; + double nm32 = m22 * view.m32() + m32; + double nm33 = 1.0; + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(PROPERTY_AFFINE); + return dest; + } + + /** + * Component-wise add the upper 4x3 submatrices of this and other + * by first multiplying each component of other's 4x3 submatrix by otherFactor and + * adding that result to this. + *

+ * The matrix other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's 4x3 components + * @return this + */ + public Matrix4d fma4x3(Matrix4dc other, double otherFactor) { + return fma4x3(other, otherFactor, this); + } + + public Matrix4d fma4x3(Matrix4dc other, double otherFactor, Matrix4d dest) { + dest._m00(Math.fma(other.m00(), otherFactor, m00)) + ._m01(Math.fma(other.m01(), otherFactor, m01)) + ._m02(Math.fma(other.m02(), otherFactor, m02)) + ._m03(m03) + ._m10(Math.fma(other.m10(), otherFactor, m10)) + ._m11(Math.fma(other.m11(), otherFactor, m11)) + ._m12(Math.fma(other.m12(), otherFactor, m12)) + ._m13(m13) + ._m20(Math.fma(other.m20(), otherFactor, m20)) + ._m21(Math.fma(other.m21(), otherFactor, m21)) + ._m22(Math.fma(other.m22(), otherFactor, m22)) + ._m23(m23) + ._m30(Math.fma(other.m30(), otherFactor, m30)) + ._m31(Math.fma(other.m31(), otherFactor, m31)) + ._m32(Math.fma(other.m32(), otherFactor, m32)) + ._m33(m33) + ._properties(0); + return dest; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix4d add(Matrix4dc other) { + return add(other, this); + } + + public Matrix4d add(Matrix4dc other, Matrix4d dest) { + dest._m00(m00 + other.m00()) + ._m01(m01 + other.m01()) + ._m02(m02 + other.m02()) + ._m03(m03 + other.m03()) + ._m10(m10 + other.m10()) + ._m11(m11 + other.m11()) + ._m12(m12 + other.m12()) + ._m13(m13 + other.m13()) + ._m20(m20 + other.m20()) + ._m21(m21 + other.m21()) + ._m22(m22 + other.m22()) + ._m23(m23 + other.m23()) + ._m30(m30 + other.m30()) + ._m31(m31 + other.m31()) + ._m32(m32 + other.m32()) + ._m33(m33 + other.m33()) + ._properties(0); + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix4d sub(Matrix4dc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix4d sub(Matrix4dc subtrahend, Matrix4d dest) { + dest._m00(m00 - subtrahend.m00()) + ._m01(m01 - subtrahend.m01()) + ._m02(m02 - subtrahend.m02()) + ._m03(m03 - subtrahend.m03()) + ._m10(m10 - subtrahend.m10()) + ._m11(m11 - subtrahend.m11()) + ._m12(m12 - subtrahend.m12()) + ._m13(m13 - subtrahend.m13()) + ._m20(m20 - subtrahend.m20()) + ._m21(m21 - subtrahend.m21()) + ._m22(m22 - subtrahend.m22()) + ._m23(m23 - subtrahend.m23()) + ._m30(m30 - subtrahend.m30()) + ._m31(m31 - subtrahend.m31()) + ._m32(m32 - subtrahend.m32()) + ._m33(m33 - subtrahend.m33()) + ._properties(0); + return dest; + } + + /** + * Component-wise multiply this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix4d mulComponentWise(Matrix4dc other) { + return mulComponentWise(other, this); + } + + public Matrix4d mulComponentWise(Matrix4dc other, Matrix4d dest) { + dest._m00(m00 * other.m00()) + ._m01(m01 * other.m01()) + ._m02(m02 * other.m02()) + ._m03(m03 * other.m03()) + ._m10(m10 * other.m10()) + ._m11(m11 * other.m11()) + ._m12(m12 * other.m12()) + ._m13(m13 * other.m13()) + ._m20(m20 * other.m20()) + ._m21(m21 * other.m21()) + ._m22(m22 * other.m22()) + ._m23(m23 * other.m23()) + ._m30(m30 * other.m30()) + ._m31(m31 * other.m31()) + ._m32(m32 * other.m32()) + ._m33(m33 * other.m33()) + ._properties(0); + return dest; + } + + /** + * Component-wise add the upper 4x3 submatrices of this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix4d add4x3(Matrix4dc other) { + return add4x3(other, this); + } + + public Matrix4d add4x3(Matrix4dc other, Matrix4d dest) { + dest._m00(m00 + other.m00()) + ._m01(m01 + other.m01()) + ._m02(m02 + other.m02()) + ._m03(m03) + ._m10(m10 + other.m10()) + ._m11(m11 + other.m11()) + ._m12(m12 + other.m12()) + ._m13(m13) + ._m20(m20 + other.m20()) + ._m21(m21 + other.m21()) + ._m22(m22 + other.m22()) + ._m23(m23) + ._m30(m30 + other.m30()) + ._m31(m31 + other.m31()) + ._m32(m32 + other.m32()) + ._m33(m33) + ._properties(0); + return dest; + } + + /** + * Component-wise add the upper 4x3 submatrices of this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix4d add4x3(Matrix4fc other) { + return add4x3(other, this); + } + + public Matrix4d add4x3(Matrix4fc other, Matrix4d dest) { + dest._m00(m00 + other.m00()) + ._m01(m01 + other.m01()) + ._m02(m02 + other.m02()) + ._m03(m03) + ._m10(m10 + other.m10()) + ._m11(m11 + other.m11()) + ._m12(m12 + other.m12()) + ._m13(m13) + ._m20(m20 + other.m20()) + ._m21(m21 + other.m21()) + ._m22(m22 + other.m22()) + ._m23(m23) + ._m30(m30 + other.m30()) + ._m31(m31 + other.m31()) + ._m32(m32 + other.m32()) + ._m33(m33) + ._properties(0); + return dest; + } + + /** + * Component-wise subtract the upper 4x3 submatrices of subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix4d sub4x3(Matrix4dc subtrahend) { + return sub4x3(subtrahend, this); + } + + public Matrix4d sub4x3(Matrix4dc subtrahend, Matrix4d dest) { + dest._m00(m00 - subtrahend.m00()) + ._m01(m01 - subtrahend.m01()) + ._m02(m02 - subtrahend.m02()) + ._m03(m03) + ._m10(m10 - subtrahend.m10()) + ._m11(m11 - subtrahend.m11()) + ._m12(m12 - subtrahend.m12()) + ._m13(m13) + ._m20(m20 - subtrahend.m20()) + ._m21(m21 - subtrahend.m21()) + ._m22(m22 - subtrahend.m22()) + ._m23(m23) + ._m30(m30 - subtrahend.m30()) + ._m31(m31 - subtrahend.m31()) + ._m32(m32 - subtrahend.m32()) + ._m33(m33) + ._properties(0); + return dest; + } + + /** + * Component-wise multiply the upper 4x3 submatrices of this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix4d mul4x3ComponentWise(Matrix4dc other) { + return mul4x3ComponentWise(other, this); + } + + public Matrix4d mul4x3ComponentWise(Matrix4dc other, Matrix4d dest) { + dest._m00(m00 * other.m00()) + ._m01(m01 * other.m01()) + ._m02(m02 * other.m02()) + ._m03(m03) + ._m10(m10 * other.m10()) + ._m11(m11 * other.m11()) + ._m12(m12 * other.m12()) + ._m13(m13) + ._m20(m20 * other.m20()) + ._m21(m21 * other.m21()) + ._m22(m22 * other.m22()) + ._m23(m23) + ._m30(m30 * other.m30()) + ._m31(m31 * other.m31()) + ._m32(m32 * other.m32()) + ._m33(m33) + ._properties(0); + return dest; + } + + /** Set the values within this matrix to the supplied double values. The matrix will look like this:

+ * + * m00, m10, m20, m30
+ * m01, m11, m21, m31
+ * m02, m12, m22, m32
+ * m03, m13, m23, m33 + * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m02 + * the new value of m02 + * @param m03 + * the new value of m03 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @param m12 + * the new value of m12 + * @param m13 + * the new value of m13 + * @param m20 + * the new value of m20 + * @param m21 + * the new value of m21 + * @param m22 + * the new value of m22 + * @param m23 + * the new value of m23 + * @param m30 + * the new value of m30 + * @param m31 + * the new value of m31 + * @param m32 + * the new value of m32 + * @param m33 + * the new value of m33 + * @return this + */ + public Matrix4d set(double m00, double m01, double m02,double m03, + double m10, double m11, double m12, double m13, + double m20, double m21, double m22, double m23, + double m30, double m31, double m32, double m33) { + this.m00 = m00; + this.m10 = m10; + this.m20 = m20; + this.m30 = m30; + this.m01 = m01; + this.m11 = m11; + this.m21 = m21; + this.m31 = m31; + this.m02 = m02; + this.m12 = m12; + this.m22 = m22; + this.m32 = m32; + this.m03 = m03; + this.m13 = m13; + this.m23 = m23; + this.m33 = m33; + return determineProperties(); + } + + /** + * Set the values in the matrix using a double array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 4, 8, 12
+ * 1, 5, 9, 13
+ * 2, 6, 10, 14
+ * 3, 7, 11, 15
+ * + * @see #set(double[]) + * + * @param m + * the array to read the matrix values from + * @param off + * the offset into the array + * @return this + */ + public Matrix4d set(double m[], int off) { + return + _m00(m[off+0]). + _m01(m[off+1]). + _m02(m[off+2]). + _m03(m[off+3]). + _m10(m[off+4]). + _m11(m[off+5]). + _m12(m[off+6]). + _m13(m[off+7]). + _m20(m[off+8]). + _m21(m[off+9]). + _m22(m[off+10]). + _m23(m[off+11]). + _m30(m[off+12]). + _m31(m[off+13]). + _m32(m[off+14]). + _m33(m[off+15]). + determineProperties(); + } + + /** + * Set the values in the matrix using a double array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 4, 8, 12
+ * 1, 5, 9, 13
+ * 2, 6, 10, 14
+ * 3, 7, 11, 15
+ * + * @see #set(double[], int) + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix4d set(double m[]) { + return set(m, 0); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 4, 8, 12
+ * 1, 5, 9, 13
+ * 2, 6, 10, 14
+ * 3, 7, 11, 15
+ * + * @see #set(float[]) + * + * @param m + * the array to read the matrix values from + * @param off + * the offset into the array + * @return this + */ + public Matrix4d set(float m[], int off) { + return + _m00(m[off+0]). + _m01(m[off+1]). + _m02(m[off+2]). + _m03(m[off+3]). + _m10(m[off+4]). + _m11(m[off+5]). + _m12(m[off+6]). + _m13(m[off+7]). + _m20(m[off+8]). + _m21(m[off+9]). + _m22(m[off+10]). + _m23(m[off+11]). + _m30(m[off+12]). + _m31(m[off+13]). + _m32(m[off+14]). + _m33(m[off+15]). + determineProperties(); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 4, 8, 12
+ * 1, 5, 9, 13
+ * 2, 6, 10, 14
+ * 3, 7, 11, 15
+ * + * @see #set(float[], int) + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix4d set(float m[]) { + return set(m, 0); + } + + /** + * Set the values of this matrix by reading 16 double values from the given {@link DoubleBuffer} in column-major order, + * starting at its current position. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4d set(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link FloatBuffer} in column-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4d set(FloatBuffer buffer) { + MemUtil.INSTANCE.getf(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 double values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4d set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 double values from the given {@link DoubleBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4d set(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link FloatBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4d set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.getf(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 double values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4d set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4d setFloats(ByteBuffer buffer) { + MemUtil.INSTANCE.getf(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4d setFloats(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.getf(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 double values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix4d setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return determineProperties(); + } + + /** + * Set the four columns of this matrix to the supplied vectors, respectively. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @param col3 + * the fourth column + * @return this + */ + public Matrix4d set(Vector4d col0, Vector4d col1, Vector4d col2, Vector4d col3) { + return + _m00(col0.x()). + _m01(col0.y()). + _m02(col0.z()). + _m03(col0.w()). + _m10(col1.x()). + _m11(col1.y()). + _m12(col1.z()). + _m13(col1.w()). + _m20(col2.x()). + _m21(col2.y()). + _m22(col2.z()). + _m23(col2.w()). + _m30(col3.x()). + _m31(col3.y()). + _m32(col3.z()). + _m33(col3.w()). + determineProperties(); + } + + public double determinant() { + if ((properties & PROPERTY_AFFINE) != 0) + return determinantAffine(); + return (m00 * m11 - m01 * m10) * (m22 * m33 - m23 * m32) + + (m02 * m10 - m00 * m12) * (m21 * m33 - m23 * m31) + + (m00 * m13 - m03 * m10) * (m21 * m32 - m22 * m31) + + (m01 * m12 - m02 * m11) * (m20 * m33 - m23 * m30) + + (m03 * m11 - m01 * m13) * (m20 * m32 - m22 * m30) + + (m02 * m13 - m03 * m12) * (m20 * m31 - m21 * m30); + } + + public double determinant3x3() { + return (m00 * m11 - m01 * m10) * m22 + + (m02 * m10 - m00 * m12) * m21 + + (m01 * m12 - m02 * m11) * m20; + } + + public double determinantAffine() { + return (m00 * m11 - m01 * m10) * m22 + + (m02 * m10 - m00 * m12) * m21 + + (m01 * m12 - m02 * m11) * m20; + } + + /** + * Invert this matrix. + *

+ * If this matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing, + * and thus its last row is equal to (0, 0, 0, 1), then {@link #invertAffine()} can be used instead of this method. + * + * @see #invertAffine() + * + * @return this + */ + public Matrix4d invert() { + return invert(this); + } + + public Matrix4d invert(Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return invertTranslation(dest); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return invertOrthonormal(dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return invertAffine(dest); + else if ((properties & PROPERTY_PERSPECTIVE) != 0) + return invertPerspective(dest); + return invertGeneric(dest); + } + private Matrix4d invertTranslation(Matrix4d dest) { + if (dest != this) + dest.set(this); + dest._m30(-m30) + ._m31(-m31) + ._m32(-m32) + ._properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return dest; + } + private Matrix4d invertOrthonormal(Matrix4d dest) { + double nm30 = -(m00 * m30 + m01 * m31 + m02 * m32); + double nm31 = -(m10 * m30 + m11 * m31 + m12 * m32); + double nm32 = -(m20 * m30 + m21 * m31 + m22 * m32); + double m01 = this.m01; + double m02 = this.m02; + double m12 = this.m12; + dest._m00(m00) + ._m01(m10) + ._m02(m20) + ._m03(0.0) + ._m10(m01) + ._m11(m11) + ._m12(m21) + ._m13(0.0) + ._m20(m02) + ._m21(m12) + ._m22(m22) + ._m23(0.0) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(1.0) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return dest; + } + private Matrix4d invertGeneric(Matrix4d dest) { + if (this != dest) + return invertGenericNonThis(dest); + return invertGenericThis(dest); + } + private Matrix4d invertGenericNonThis(Matrix4d dest) { + double a = m00 * m11 - m01 * m10; + double b = m00 * m12 - m02 * m10; + double c = m00 * m13 - m03 * m10; + double d = m01 * m12 - m02 * m11; + double e = m01 * m13 - m03 * m11; + double f = m02 * m13 - m03 * m12; + double g = m20 * m31 - m21 * m30; + double h = m20 * m32 - m22 * m30; + double i = m20 * m33 - m23 * m30; + double j = m21 * m32 - m22 * m31; + double k = m21 * m33 - m23 * m31; + double l = m22 * m33 - m23 * m32; + double det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0 / det; + return dest + ._m00(Math.fma( m11, l, Math.fma(-m12, k, m13 * j)) * det) + ._m01(Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det) + ._m02(Math.fma( m31, f, Math.fma(-m32, e, m33 * d)) * det) + ._m03(Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det) + ._m10(Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det) + ._m11(Math.fma( m00, l, Math.fma(-m02, i, m03 * h)) * det) + ._m12(Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det) + ._m13(Math.fma( m20, f, Math.fma(-m22, c, m23 * b)) * det) + ._m20(Math.fma( m10, k, Math.fma(-m11, i, m13 * g)) * det) + ._m21(Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det) + ._m22(Math.fma( m30, e, Math.fma(-m31, c, m33 * a)) * det) + ._m23(Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det) + ._m30(Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det) + ._m31(Math.fma( m00, j, Math.fma(-m01, h, m02 * g)) * det) + ._m32(Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det) + ._m33(Math.fma( m20, d, Math.fma(-m21, b, m22 * a)) * det) + ._properties(0); + } + private Matrix4d invertGenericThis(Matrix4d dest) { + double a = m00 * m11 - m01 * m10; + double b = m00 * m12 - m02 * m10; + double c = m00 * m13 - m03 * m10; + double d = m01 * m12 - m02 * m11; + double e = m01 * m13 - m03 * m11; + double f = m02 * m13 - m03 * m12; + double g = m20 * m31 - m21 * m30; + double h = m20 * m32 - m22 * m30; + double i = m20 * m33 - m23 * m30; + double j = m21 * m32 - m22 * m31; + double k = m21 * m33 - m23 * m31; + double l = m22 * m33 - m23 * m32; + double det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0 / det; + double nm00 = Math.fma( m11, l, Math.fma(-m12, k, m13 * j)) * det; + double nm01 = Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det; + double nm02 = Math.fma( m31, f, Math.fma(-m32, e, m33 * d)) * det; + double nm03 = Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det; + double nm10 = Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det; + double nm11 = Math.fma( m00, l, Math.fma(-m02, i, m03 * h)) * det; + double nm12 = Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det; + double nm13 = Math.fma( m20, f, Math.fma(-m22, c, m23 * b)) * det; + double nm20 = Math.fma( m10, k, Math.fma(-m11, i, m13 * g)) * det; + double nm21 = Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det; + double nm22 = Math.fma( m30, e, Math.fma(-m31, c, m33 * a)) * det; + double nm23 = Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det; + double nm30 = Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det; + double nm31 = Math.fma( m00, j, Math.fma(-m01, h, m02 * g)) * det; + double nm32 = Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det; + double nm33 = Math.fma( m20, d, Math.fma(-m21, b, m22 * a)) * det; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + public Matrix4d invertPerspective(Matrix4d dest) { + double a = 1.0 / (m00 * m11); + double l = -1.0 / (m23 * m32); + dest.set(m11 * a, 0, 0, 0, + 0, m00 * a, 0, 0, + 0, 0, 0, -m23 * l, + 0, 0, -m32 * l, m22 * l); + return dest; + } + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(double, double, double, double) perspective()} methods + * or via {@link #setPerspective(double, double, double, double) setPerspective()}, that is, if this is a symmetrical perspective frustum transformation, + * then this method builds the inverse of this. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix when being obtained via {@link #perspective(double, double, double, double) perspective()}. + * + * @see #perspective(double, double, double, double) + * + * @return this + */ + public Matrix4d invertPerspective() { + return invertPerspective(this); + } + + public Matrix4d invertFrustum(Matrix4d dest) { + double invM00 = 1.0 / m00; + double invM11 = 1.0 / m11; + double invM23 = 1.0 / m23; + double invM32 = 1.0 / m32; + dest.set(invM00, 0, 0, 0, + 0, invM11, 0, 0, + 0, 0, 0, invM32, + -m20 * invM00 * invM23, -m21 * invM11 * invM23, invM23, -m22 * invM23 * invM32); + return dest; + } + + /** + * If this is an arbitrary perspective projection matrix obtained via one of the {@link #frustum(double, double, double, double, double, double) frustum()} methods + * or via {@link #setFrustum(double, double, double, double, double, double) setFrustum()}, + * then this method builds the inverse of this. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix. + *

+ * If this matrix represents a symmetric perspective frustum transformation, as obtained via {@link #perspective(double, double, double, double) perspective()}, then + * {@link #invertPerspective()} should be used instead. + * + * @see #frustum(double, double, double, double, double, double) + * @see #invertPerspective() + * + * @return this + */ + public Matrix4d invertFrustum() { + return invertFrustum(this); + } + + public Matrix4d invertOrtho(Matrix4d dest) { + double invM00 = 1.0 / m00; + double invM11 = 1.0 / m11; + double invM22 = 1.0 / m22; + dest.set(invM00, 0, 0, 0, + 0, invM11, 0, 0, + 0, 0, invM22, 0, + -m30 * invM00, -m31 * invM11, -m32 * invM22, 1) + ._properties(PROPERTY_AFFINE | (this.properties & PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Invert this orthographic projection matrix. + *

+ * This method can be used to quickly obtain the inverse of an orthographic projection matrix. + * + * @return this + */ + public Matrix4d invertOrtho() { + return invertOrtho(this); + } + + public Matrix4d invertPerspectiveView(Matrix4dc view, Matrix4d dest) { + double a = 1.0 / (m00 * m11); + double l = -1.0 / (m23 * m32); + double pm00 = m11 * a; + double pm11 = m00 * a; + double pm23 = -m23 * l; + double pm32 = -m32 * l; + double pm33 = m22 * l; + double vm30 = -view.m00() * view.m30() - view.m01() * view.m31() - view.m02() * view.m32(); + double vm31 = -view.m10() * view.m30() - view.m11() * view.m31() - view.m12() * view.m32(); + double vm32 = -view.m20() * view.m30() - view.m21() * view.m31() - view.m22() * view.m32(); + double nm10 = view.m01() * pm11; + double nm30 = view.m02() * pm32 + vm30 * pm33; + double nm31 = view.m12() * pm32 + vm31 * pm33; + double nm32 = view.m22() * pm32 + vm32 * pm33; + return dest + ._m00(view.m00() * pm00) + ._m01(view.m10() * pm00) + ._m02(view.m20() * pm00) + ._m03(0.0) + ._m10(nm10) + ._m11(view.m11() * pm11) + ._m12(view.m21() * pm11) + ._m13(0.0) + ._m20(vm30 * pm23) + ._m21(vm31 * pm23) + ._m22(vm32 * pm23) + ._m23(pm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(pm33) + ._properties(0); + } + + public Matrix4d invertPerspectiveView(Matrix4x3dc view, Matrix4d dest) { + double a = 1.0 / (m00 * m11); + double l = -1.0 / (m23 * m32); + double pm00 = m11 * a; + double pm11 = m00 * a; + double pm23 = -m23 * l; + double pm32 = -m32 * l; + double pm33 = m22 * l; + double vm30 = -view.m00() * view.m30() - view.m01() * view.m31() - view.m02() * view.m32(); + double vm31 = -view.m10() * view.m30() - view.m11() * view.m31() - view.m12() * view.m32(); + double vm32 = -view.m20() * view.m30() - view.m21() * view.m31() - view.m22() * view.m32(); + return dest + ._m00(view.m00() * pm00) + ._m01(view.m10() * pm00) + ._m02(view.m20() * pm00) + ._m03(0.0) + ._m10(view.m01() * pm11) + ._m11(view.m11() * pm11) + ._m12(view.m21() * pm11) + ._m13(0.0) + ._m20(vm30 * pm23) + ._m21(vm31 * pm23) + ._m22(vm32 * pm23) + ._m23(pm23) + ._m30(view.m02() * pm32 + vm30 * pm33) + ._m31(view.m12() * pm32 + vm31 * pm33) + ._m32(view.m22() * pm32 + vm32 * pm33) + ._m33(pm33) + ._properties(0); + } + + public Matrix4d invertAffine(Matrix4d dest) { + double m11m00 = m00 * m11, m10m01 = m01 * m10, m10m02 = m02 * m10; + double m12m00 = m00 * m12, m12m01 = m01 * m12, m11m02 = m02 * m11; + double s = 1.0 / ((m11m00 - m10m01) * m22 + (m10m02 - m12m00) * m21 + (m12m01 - m11m02) * m20); + double m10m22 = m10 * m22, m10m21 = m10 * m21, m11m22 = m11 * m22; + double m11m20 = m11 * m20, m12m21 = m12 * m21, m12m20 = m12 * m20; + double m20m02 = m20 * m02, m20m01 = m20 * m01, m21m02 = m21 * m02; + double m21m00 = m21 * m00, m22m01 = m22 * m01, m22m00 = m22 * m00; + double nm00 = (m11m22 - m12m21) * s; + double nm01 = (m21m02 - m22m01) * s; + double nm02 = (m12m01 - m11m02) * s; + double nm10 = (m12m20 - m10m22) * s; + double nm11 = (m22m00 - m20m02) * s; + double nm12 = (m10m02 - m12m00) * s; + double nm20 = (m10m21 - m11m20) * s; + double nm21 = (m20m01 - m21m00) * s; + double nm22 = (m11m00 - m10m01) * s; + double nm30 = (m10m22 * m31 - m10m21 * m32 + m11m20 * m32 - m11m22 * m30 + m12m21 * m30 - m12m20 * m31) * s; + double nm31 = (m20m02 * m31 - m20m01 * m32 + m21m00 * m32 - m21m02 * m30 + m22m01 * m30 - m22m00 * m31) * s; + double nm32 = (m11m02 * m30 - m12m01 * m30 + m12m00 * m31 - m10m02 * m31 + m10m01 * m32 - m11m00 * m32) * s; + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(0.0) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(1.0) + ._properties(PROPERTY_AFFINE); + return dest; + } + + /** + * Invert this matrix by assuming that it is an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)). + * + * @return this + */ + public Matrix4d invertAffine() { + return invertAffine(this); + } + + /** + * Transpose this matrix. + * + * @return this + */ + public Matrix4d transpose() { + return transpose(this); + } + + public Matrix4d transpose(Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if (this != dest) + return transposeNonThisGeneric(dest); + return transposeThisGeneric(dest); + } + private Matrix4d transposeNonThisGeneric(Matrix4d dest) { + return dest + ._m00(m00) + ._m01(m10) + ._m02(m20) + ._m03(m30) + ._m10(m01) + ._m11(m11) + ._m12(m21) + ._m13(m31) + ._m20(m02) + ._m21(m12) + ._m22(m22) + ._m23(m32) + ._m30(m03) + ._m31(m13) + ._m32(m23) + ._m33(m33) + ._properties(0); + } + private Matrix4d transposeThisGeneric(Matrix4d dest) { + double nm10 = m01; + double nm20 = m02; + double nm21 = m12; + double nm30 = m03; + double nm31 = m13; + double nm32 = m23; + return dest + ._m01(m10) + ._m02(m20) + ._m03(m30) + ._m10(nm10) + ._m12(m21) + ._m13(m31) + ._m20(nm20) + ._m21(nm21) + ._m23(m32) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._properties(0); + } + + /** + * Transpose only the upper left 3x3 submatrix of this matrix. + *

+ * All other matrix elements are left unchanged. + * + * @return this + */ + public Matrix4d transpose3x3() { + return transpose3x3(this); + } + + public Matrix4d transpose3x3(Matrix4d dest) { + double nm10 = m01, nm20 = m02, nm21 = m12; + return dest + ._m00(m00) + ._m01(m10) + ._m02(m20) + ._m10(nm10) + ._m11(m11) + ._m12(m21) + ._m20(nm20) + ._m21(nm21) + ._m22(m22) + ._properties(this.properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + public Matrix3d transpose3x3(Matrix3d dest) { + return dest + ._m00(m00) + ._m01(m10) + ._m02(m20) + ._m10(m01) + ._m11(m11) + ._m12(m21) + ._m20(m02) + ._m21(m12) + ._m22(m22); + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4d translation(double x, double y, double z) { + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + return this. + _m30(x). + _m31(y). + _m32(z). + _m33(1.0). + _properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + * + * @param offset + * the offsets in x, y and z to translate + * @return this + */ + public Matrix4d translation(Vector3fc offset) { + return translation(offset.x(), offset.y(), offset.z()); + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + * + * @param offset + * the offsets in x, y and z to translate + * @return this + */ + public Matrix4d translation(Vector3dc offset) { + return translation(offset.x(), offset.y(), offset.z()); + } + + /** + * Set only the translation components (m30, m31, m32) of this matrix to the given values (x, y, z). + *

+ * To build a translation matrix instead, use {@link #translation(double, double, double)}. + * To apply a translation, use {@link #translate(double, double, double)}. + * + * @see #translation(double, double, double) + * @see #translate(double, double, double) + * + * @param x + * the units to translate in x + * @param y + * the units to translate in y + * @param z + * the units to translate in z + * @return this + */ + public Matrix4d setTranslation(double x, double y, double z) { + _m30(x). + _m31(y). + _m32(z). + properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY); + return this; + } + + /** + * Set only the translation components (m30, m31, m32) of this matrix to the given values (xyz.x, xyz.y, xyz.z). + *

+ * To build a translation matrix instead, use {@link #translation(Vector3dc)}. + * To apply a translation, use {@link #translate(Vector3dc)}. + * + * @see #translation(Vector3dc) + * @see #translate(Vector3dc) + * + * @param xyz + * the units to translate in (x, y, z) + * @return this + */ + public Matrix4d setTranslation(Vector3dc xyz) { + return setTranslation(xyz.x(), xyz.y(), xyz.z()); + } + + public Vector3d getTranslation(Vector3d dest) { + dest.x = m30; + dest.y = m31; + dest.z = m32; + return dest; + } + + public Vector3d getScale(Vector3d dest) { + dest.x = Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02); + dest.y = Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12); + dest.z = Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22); + return dest; + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + " " + Runtime.format(m20, formatter) + " " + Runtime.format(m30, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + " " + Runtime.format(m21, formatter) + " " + Runtime.format(m31, formatter) + "\n" + + Runtime.format(m02, formatter) + " " + Runtime.format(m12, formatter) + " " + Runtime.format(m22, formatter) + " " + Runtime.format(m32, formatter) + "\n" + + Runtime.format(m03, formatter) + " " + Runtime.format(m13, formatter) + " " + Runtime.format(m23, formatter) + " " + Runtime.format(m33, formatter) + "\n"; + } + + public Matrix4d get(Matrix4d dest) { + return dest.set(this); + } + + public Matrix4x3d get4x3(Matrix4x3d dest) { + return dest.set(this); + } + + public Matrix3d get3x3(Matrix3d dest) { + return dest.set(this); + } + + public Quaternionf getUnnormalizedRotation(Quaternionf dest) { + return dest.setFromUnnormalized(this); + } + + public Quaternionf getNormalizedRotation(Quaternionf dest) { + return dest.setFromNormalized(this); + } + + public Quaterniond getUnnormalizedRotation(Quaterniond dest) { + return dest.setFromUnnormalized(this); + } + + public Quaterniond getNormalizedRotation(Quaterniond dest) { + return dest.setFromNormalized(this); + } + + public DoubleBuffer get(DoubleBuffer dest) { + MemUtil.INSTANCE.put(this, dest.position(), dest); + return dest; + } + + public DoubleBuffer get(int index, DoubleBuffer dest) { + MemUtil.INSTANCE.put(this, index, dest); + return dest; + } + + public FloatBuffer get(FloatBuffer dest) { + MemUtil.INSTANCE.putf(this, dest.position(), dest); + return dest; + } + + public FloatBuffer get(int index, FloatBuffer dest) { + MemUtil.INSTANCE.putf(this, index, dest); + return dest; + } + + public ByteBuffer get(ByteBuffer dest) { + MemUtil.INSTANCE.put(this, dest.position(), dest); + return dest; + } + + public ByteBuffer get(int index, ByteBuffer dest) { + MemUtil.INSTANCE.put(this, index, dest); + return dest; + } + + public ByteBuffer getFloats(ByteBuffer dest) { + MemUtil.INSTANCE.putf(this, dest.position(), dest); + return dest; + } + + public ByteBuffer getFloats(int index, ByteBuffer dest) { + MemUtil.INSTANCE.putf(this, index, dest); + return dest; + } + public Matrix4dc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + public double[] get(double[] dest, int offset) { + dest[offset+0] = m00; + dest[offset+1] = m01; + dest[offset+2] = m02; + dest[offset+3] = m03; + dest[offset+4] = m10; + dest[offset+5] = m11; + dest[offset+6] = m12; + dest[offset+7] = m13; + dest[offset+8] = m20; + dest[offset+9] = m21; + dest[offset+10] = m22; + dest[offset+11] = m23; + dest[offset+12] = m30; + dest[offset+13] = m31; + dest[offset+14] = m32; + dest[offset+15] = m33; + return dest; + } + + public double[] get(double[] dest) { + return get(dest, 0); + } + + public float[] get(float[] dest, int offset) { + dest[offset+0] = (float)m00; + dest[offset+1] = (float)m01; + dest[offset+2] = (float)m02; + dest[offset+3] = (float)m03; + dest[offset+4] = (float)m10; + dest[offset+5] = (float)m11; + dest[offset+6] = (float)m12; + dest[offset+7] = (float)m13; + dest[offset+8] = (float)m20; + dest[offset+9] = (float)m21; + dest[offset+10] = (float)m22; + dest[offset+11] = (float)m23; + dest[offset+12] = (float)m30; + dest[offset+13] = (float)m31; + dest[offset+14] = (float)m32; + dest[offset+15] = (float)m33; + return dest; + } + + public float[] get(float[] dest) { + return get(dest, 0); + } + + public DoubleBuffer getTransposed(DoubleBuffer dest) { + MemUtil.INSTANCE.putTransposed(this, dest.position(), dest); + return dest; + } + + public DoubleBuffer getTransposed(int index, DoubleBuffer dest) { + MemUtil.INSTANCE.putTransposed(this, index, dest); + return dest; + } + + public ByteBuffer getTransposed(ByteBuffer dest) { + MemUtil.INSTANCE.putTransposed(this, dest.position(), dest); + return dest; + } + + public ByteBuffer getTransposed(int index, ByteBuffer dest) { + MemUtil.INSTANCE.putTransposed(this, index, dest); + return dest; + } + + public DoubleBuffer get4x3Transposed(DoubleBuffer dest) { + MemUtil.INSTANCE.put4x3Transposed(this, dest.position(), dest); + return dest; + } + + public DoubleBuffer get4x3Transposed(int index, DoubleBuffer dest) { + MemUtil.INSTANCE.put4x3Transposed(this, index, dest); + return dest; + } + + public ByteBuffer get4x3Transposed(ByteBuffer dest) { + MemUtil.INSTANCE.put4x3Transposed(this, dest.position(), dest); + return dest; + } + + public ByteBuffer get4x3Transposed(int index, ByteBuffer dest) { + MemUtil.INSTANCE.put4x3Transposed(this, index, dest); + return dest; + } + + /** + * Set all the values within this matrix to 0. + * + * @return this + */ + public Matrix4d zero() { + return + _m00(0.0). + _m01(0.0). + _m02(0.0). + _m03(0.0). + _m10(0.0). + _m11(0.0). + _m12(0.0). + _m13(0.0). + _m20(0.0). + _m21(0.0). + _m22(0.0). + _m23(0.0). + _m30(0.0). + _m31(0.0). + _m32(0.0). + _m33(0.0). + _properties(0); + } + + /** + * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(double) scale()} instead. + * + * @see #scale(double) + * + * @param factor + * the scale factor in x, y and z + * @return this + */ + public Matrix4d scaling(double factor) { + return scaling(factor, factor, factor); + } + + /** + * Set this matrix to be a simple scale matrix. + * + * @param x + * the scale in x + * @param y + * the scale in y + * @param z + * the scale in z + * @return this + */ + public Matrix4d scaling(double x, double y, double z) { + if ((properties & PROPERTY_IDENTITY) == 0) + identity(); + boolean one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z); + _m00(x). + _m11(y). + _m22(z). + properties = PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0); + return this; + } + + /** + * Set this matrix to be a simple scale matrix which scales the base axes by + * xyz.x, xyz.y and xyz.z, respectively. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix use {@link #scale(Vector3dc) scale()} instead. + * + * @see #scale(Vector3dc) + * + * @param xyz + * the scale in x, y and z, respectively + * @return this + */ + public Matrix4d scaling(Vector3dc xyz) { + return scaling(xyz.x(), xyz.y(), xyz.z()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * From Wikipedia + * + * @param angle + * the angle in radians + * @param x + * the x-coordinate of the axis to rotate about + * @param y + * the y-coordinate of the axis to rotate about + * @param z + * the z-coordinate of the axis to rotate about + * @return this + */ + public Matrix4d rotation(double angle, double x, double y, double z) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotationX(x * angle); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotationY(y * angle); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotationZ(z * angle); + return rotationInternal(angle, x, y, z); + } + private Matrix4d rotationInternal(double angle, double x, double y, double z) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + double C = 1.0 - cos; + double xy = x * y, xz = x * z, yz = y * z; + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + _m00(cos + x * x * C). + _m10(xy * C - z * sin). + _m20(xz * C + y * sin). + _m01(xy * C + z * sin). + _m11(cos + y * y * C). + _m21(yz * C - x * sin). + _m02(xz * C - y * sin). + _m12(yz * C + x * sin). + _m22(cos + z * z * C). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4d rotationX(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + _m11(cos). + _m12(sin). + _m21(-sin). + _m22(cos). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4d rotationY(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + _m00(cos). + _m02(-sin). + _m20(sin). + _m22(cos). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4d rotationZ(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + _m00(cos). + _m01(sin). + _m10(-sin). + _m11(cos). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Z axis to align the local +X towards (dirX, dirY). + *

+ * The vector (dirX, dirY) must be a unit vector. + * + * @param dirX + * the x component of the normalized direction + * @param dirY + * the y component of the normalized direction + * @return this + */ + public Matrix4d rotationTowardsXY(double dirX, double dirY) { + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + this.m00 = dirY; + this.m01 = dirX; + this.m10 = -dirX; + this.m11 = dirY; + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4d rotationXYZ(double angleX, double angleY, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + + // rotateX + double nm11 = cosX; + double nm12 = sinX; + double nm21 = m_sinX; + double nm22 = cosX; + // rotateY + double nm00 = cosY; + double nm01 = nm21 * m_sinY; + double nm02 = nm22 * m_sinY; + _m20(sinY). + _m21(nm21 * cosY). + _m22(nm22 * cosY). + // rotateZ + _m00(nm00 * cosZ). + _m01(nm01 * cosZ + nm11 * sinZ). + _m02(nm02 * cosZ + nm12 * sinZ). + _m10(nm00 * m_sinZ). + _m11(nm01 * m_sinZ + nm11 * cosZ). + _m12(nm02 * m_sinZ + nm12 * cosZ). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4d rotationZYX(double angleZ, double angleY, double angleX) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + + // rotateZ + double nm00 = cosZ; + double nm01 = sinZ; + double nm10 = m_sinZ; + double nm11 = cosZ; + // rotateY + double nm20 = nm00 * sinY; + double nm21 = nm01 * sinY; + double nm22 = cosY; + _m00(nm00 * cosY). + _m01(nm01 * cosY). + _m02(m_sinY). + // rotateX + _m10(nm10 * cosX + nm20 * sinX). + _m11(nm11 * cosX + nm21 * sinX). + _m12(nm22 * sinX). + _m20(nm10 * m_sinX + nm20 * cosX). + _m21(nm11 * m_sinX + nm21 * cosX). + _m22(nm22 * cosX). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4d rotationYXZ(double angleY, double angleX, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm00 = cosY; + double nm02 = m_sinY; + double nm20 = sinY; + double nm22 = cosY; + // rotateX + double nm10 = nm20 * sinX; + double nm11 = cosX; + double nm12 = nm22 * sinX; + _m20(nm20 * cosX). + _m21(m_sinX). + _m22(nm22 * cosX). + _m23(0.0). + // rotateZ + _m00(nm00 * cosZ + nm10 * sinZ). + _m01(nm11 * sinZ). + _m02(nm02 * cosZ + nm12 * sinZ). + _m03(0.0). + _m10(nm00 * m_sinZ + nm10 * cosZ). + _m11(nm11 * cosZ). + _m12(nm02 * m_sinZ + nm12 * cosZ). + _m13(0.0). + // set last column to identity + _m30(0.0). + _m31(0.0). + _m32(0.0). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set only the upper left 3x3 submatrix of this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4d setRotationXYZ(double angleX, double angleY, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + + // rotateX + double nm11 = cosX; + double nm12 = sinX; + double nm21 = m_sinX; + double nm22 = cosX; + // rotateY + double nm00 = cosY; + double nm01 = nm21 * m_sinY; + double nm02 = nm22 * m_sinY; + _m20(sinY). + _m21(nm21 * cosY). + _m22(nm22 * cosY). + // rotateZ + _m00(nm00 * cosZ). + _m01(nm01 * cosZ + nm11 * sinZ). + _m02(nm02 * cosZ + nm12 * sinZ). + _m10(nm00 * m_sinZ). + _m11(nm01 * m_sinZ + nm11 * cosZ). + _m12(nm02 * m_sinZ + nm12 * cosZ). + properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set only the upper left 3x3 submatrix of this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4d setRotationZYX(double angleZ, double angleY, double angleX) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + + // rotateZ + double nm00 = cosZ; + double nm01 = sinZ; + double nm10 = m_sinZ; + double nm11 = cosZ; + // rotateY + double nm20 = nm00 * sinY; + double nm21 = nm01 * sinY; + double nm22 = cosY; + _m00(nm00 * cosY). + _m01(nm01 * cosY). + _m02(m_sinY). + // rotateX + _m10(nm10 * cosX + nm20 * sinX). + _m11(nm11 * cosX + nm21 * sinX). + _m12(nm22 * sinX). + _m20(nm10 * m_sinX + nm20 * cosX). + _m21(nm11 * m_sinX + nm21 * cosX). + _m22(nm22 * cosX). + properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set only the upper left 3x3 submatrix of this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4d setRotationYXZ(double angleY, double angleX, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm00 = cosY; + double nm02 = m_sinY; + double nm20 = sinY; + double nm22 = cosY; + // rotateX + double nm10 = nm20 * sinX; + double nm11 = cosX; + double nm12 = nm22 * sinX; + _m20(nm20 * cosX). + _m21(m_sinX). + _m22(nm22 * cosX). + // rotateZ + _m00(nm00 * cosZ + nm10 * sinZ). + _m01(nm11 * sinZ). + _m02(nm02 * cosZ + nm12 * sinZ). + _m10(nm00 * m_sinZ + nm10 * cosZ). + _m11(nm11 * cosZ). + _m12(nm02 * m_sinZ + nm12 * cosZ). + properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about + * @return this + */ + public Matrix4d rotation(double angle, Vector3dc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about + * @return this + */ + public Matrix4d rotation(double angle, Vector3fc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + public Vector4d transform(Vector4d v) { + return v.mul(this); + } + + public Vector4d transform(Vector4dc v, Vector4d dest) { + return v.mul(this, dest); + } + + public Vector4d transform(double x, double y, double z, double w, Vector4d dest) { + return dest.set(m00 * x + m10 * y + m20 * z + m30 * w, + m01 * x + m11 * y + m21 * z + m31 * w, + m02 * x + m12 * y + m22 * z + m32 * w, + m03 * x + m13 * y + m23 * z + m33 * w); + } + + public Vector4d transformTranspose(Vector4d v) { + return v.mulTranspose(this); + } + public Vector4d transformTranspose(Vector4dc v, Vector4d dest) { + return v.mulTranspose(this, dest); + } + public Vector4d transformTranspose(double x, double y, double z, double w, Vector4d dest) { + return dest.set(x, y, z, w).mulTranspose(this); + } + + public Vector4d transformProject(Vector4d v) { + return v.mulProject(this); + } + + public Vector4d transformProject(Vector4dc v, Vector4d dest) { + return v.mulProject(this, dest); + } + + public Vector4d transformProject(double x, double y, double z, double w, Vector4d dest) { + double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33 * w); + return dest.set((m00 * x + m10 * y + m20 * z + m30 * w) * invW, + (m01 * x + m11 * y + m21 * z + m31 * w) * invW, + (m02 * x + m12 * y + m22 * z + m32 * w) * invW, + 1.0); + } + + public Vector3d transformProject(Vector3d v) { + return v.mulProject(this); + } + + public Vector3d transformProject(Vector3dc v, Vector3d dest) { + return v.mulProject(this, dest); + } + + public Vector3d transformProject(double x, double y, double z, Vector3d dest) { + double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33); + return dest.set((m00 * x + m10 * y + m20 * z + m30) * invW, + (m01 * x + m11 * y + m21 * z + m31) * invW, + (m02 * x + m12 * y + m22 * z + m32) * invW); + } + + public Vector3d transformProject(Vector4dc v, Vector3d dest) { + return v.mulProject(this, dest); + } + + public Vector3d transformProject(double x, double y, double z, double w, Vector3d dest) { + dest.x = x; + dest.y = y; + dest.z = z; + return dest.mulProject(this, w, dest); + } + + public Vector3d transformPosition(Vector3d dest) { + return dest.set(m00 * dest.x + m10 * dest.y + m20 * dest.z + m30, + m01 * dest.x + m11 * dest.y + m21 * dest.z + m31, + m02 * dest.x + m12 * dest.y + m22 * dest.z + m32); + } + + public Vector3d transformPosition(Vector3dc v, Vector3d dest) { + return transformPosition(v.x(), v.y(), v.z(), dest); + } + + public Vector3d transformPosition(double x, double y, double z, Vector3d dest) { + return dest.set(m00 * x + m10 * y + m20 * z + m30, + m01 * x + m11 * y + m21 * z + m31, + m02 * x + m12 * y + m22 * z + m32); + } + + public Vector3d transformDirection(Vector3d dest) { + return dest.set(m00 * dest.x + m10 * dest.y + m20 * dest.z, + m01 * dest.x + m11 * dest.y + m21 * dest.z, + m02 * dest.x + m12 * dest.y + m22 * dest.z); + } + + public Vector3d transformDirection(Vector3dc v, Vector3d dest) { + return dest.set(m00 * v.x() + m10 * v.y() + m20 * v.z(), + m01 * v.x() + m11 * v.y() + m21 * v.z(), + m02 * v.x() + m12 * v.y() + m22 * v.z()); + } + + public Vector3d transformDirection(double x, double y, double z, Vector3d dest) { + return dest.set(m00 * x + m10 * y + m20 * z, + m01 * x + m11 * y + m21 * z, + m02 * x + m12 * y + m22 * z); + } + + public Vector3f transformDirection(Vector3f dest) { + return dest.mulDirection(this); + } + + public Vector3f transformDirection(Vector3fc v, Vector3f dest) { + return v.mulDirection(this, dest); + } + + public Vector3f transformDirection(double x, double y, double z, Vector3f dest) { + float rx = (float)(m00 * x + m10 * y + m20 * z); + float ry = (float)(m01 * x + m11 * y + m21 * z); + float rz = (float)(m02 * x + m12 * y + m22 * z); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector4d transformAffine(Vector4d dest) { + return dest.mulAffine(this, dest); + } + + public Vector4d transformAffine(Vector4dc v, Vector4d dest) { + return transformAffine(v.x(), v.y(), v.z(), v.w(), dest); + } + + public Vector4d transformAffine(double x, double y, double z, double w, Vector4d dest) { + double rx = m00 * x + m10 * y + m20 * z + m30 * w; + double ry = m01 * x + m11 * y + m21 * z + m31 * w; + double rz = m02 * x + m12 * y + m22 * z + m32 * w; + dest.x = rx; + dest.y = ry; + dest.z = rz; + dest.w = w; + return dest; + } + + /** + * Set the upper left 3x3 submatrix of this {@link Matrix4d} to the given {@link Matrix3dc} and don't change the other elements. + * + * @param mat + * the 3x3 matrix + * @return this + */ + public Matrix4d set3x3(Matrix3dc mat) { + return + _m00(mat.m00()). + _m01(mat.m01()). + _m02(mat.m02()). + _m10(mat.m10()). + _m11(mat.m11()). + _m12(mat.m12()). + _m20(mat.m20()). + _m21(mat.m21()). + _m22(mat.m22()). + _properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + } + + public Matrix4d scale(Vector3dc xyz, Matrix4d dest) { + return scale(xyz.x(), xyz.y(), xyz.z(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @return this + */ + public Matrix4d scale(Vector3dc xyz) { + return scale(xyz.x(), xyz.y(), xyz.z(), this); + } + + public Matrix4d scale(double x, double y, double z, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.scaling(x, y, z); + return scaleGeneric(x, y, z, dest); + } + private Matrix4d scaleGeneric(double x, double y, double z, Matrix4d dest) { + boolean one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z); + dest._m00(m00 * x) + ._m01(m01 * x) + ._m02(m02 * x) + ._m03(m03 * x) + ._m10(m10 * y) + ._m11(m11 * y) + ._m12(m12 * y) + ._m13(m13 * y) + ._m20(m20 * z) + ._m21(m21 * z) + ._m22(m22 * z) + ._m23(m23 * z) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties + & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | (one ? 0 : PROPERTY_ORTHONORMAL))); + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix4d scale(double x, double y, double z) { + return scale(x, y, z, this); + } + + public Matrix4d scale(double xyz, Matrix4d dest) { + return scale(xyz, xyz, xyz, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(double, double, double) + * + * @param xyz + * the factor for all components + * @return this + */ + public Matrix4d scale(double xyz) { + return scale(xyz, xyz, xyz); + } + + public Matrix4d scaleXY(double x, double y, Matrix4d dest) { + return scale(x, y, 1.0, dest); + } + + /** + * Apply scaling to this matrix by scaling the X axis by x and the Y axis by y. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix4d scaleXY(double x, double y) { + return scale(x, y, 1.0); + } + + public Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz, Matrix4d dest) { + double nm30 = m00 * ox + m10 * oy + m20 * oz + m30; + double nm31 = m01 * ox + m11 * oy + m21 * oz + m31; + double nm32 = m02 * ox + m12 * oy + m22 * oz + m32; + double nm33 = m03 * ox + m13 * oy + m23 * oz + m33; + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + return dest + ._m00(m00 * sx) + ._m01(m01 * sx) + ._m02(m02 * sx) + ._m03(m03 * sx) + ._m10(m10 * sy) + ._m11(m11 * sy) + ._m12(m12 * sy) + ._m13(m13 * sy) + ._m20(m20 * sz) + ._m21(m21 * sz) + ._m22(m22 * sz) + ._m23(m23 * sz) + ._m30(-dest.m00 * ox - dest.m10 * oy - dest.m20 * oz + nm30) + ._m31(-dest.m01 * ox - dest.m11 * oy - dest.m21 * oz + nm31) + ._m32(-dest.m02 * ox - dest.m12 * oy - dest.m22 * oz + nm32) + ._m33(-dest.m03 * ox - dest.m13 * oy - dest.m23 * oz + nm33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | (one ? 0 : PROPERTY_ORTHONORMAL))); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz) { + return scaleAround(sx, sy, sz, ox, oy, oz, this); + } + + /** + * Apply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4d scaleAround(double factor, double ox, double oy, double oz) { + return scaleAround(factor, factor, factor, ox, oy, oz, this); + } + + public Matrix4d scaleAround(double factor, double ox, double oy, double oz, Matrix4d dest) { + return scaleAround(factor, factor, factor, ox, oy, oz, dest); + } + + public Matrix4d scaleLocal(double x, double y, double z, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.scaling(x, y, z); + return scaleLocalGeneric(x, y, z, dest); + } + private Matrix4d scaleLocalGeneric(double x, double y, double z, Matrix4d dest) { + double nm00 = x * m00; + double nm01 = y * m01; + double nm02 = z * m02; + double nm10 = x * m10; + double nm11 = y * m11; + double nm12 = z * m12; + double nm20 = x * m20; + double nm21 = y * m21; + double nm22 = z * m22; + double nm30 = x * m30; + double nm31 = y * m31; + double nm32 = z * m32; + boolean one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z); + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(m03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(m13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | (one ? 0 : PROPERTY_ORTHONORMAL))); + return dest; + } + + public Matrix4d scaleLocal(double xyz, Matrix4d dest) { + return scaleLocal(xyz, xyz, xyz, dest); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param xyz + * the factor of the x, y and z component + * @return this + */ + public Matrix4d scaleLocal(double xyz) { + return scaleLocal(xyz, this); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix4d scaleLocal(double x, double y, double z) { + return scaleLocal(x, y, z, this); + } + + public Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz, Matrix4d dest) { + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + dest._m00(sx * (m00 - ox * m03) + ox * m03) + ._m01(sy * (m01 - oy * m03) + oy * m03) + ._m02(sz * (m02 - oz * m03) + oz * m03) + ._m03(m03) + ._m10(sx * (m10 - ox * m13) + ox * m13) + ._m11(sy * (m11 - oy * m13) + oy * m13) + ._m12(sz * (m12 - oz * m13) + oz * m13) + ._m13(m13) + ._m20(sx * (m20 - ox * m23) + ox * m23) + ._m21(sy * (m21 - oy * m23) + oy * m23) + ._m22(sz * (m22 - oz * m23) + oz * m23) + ._m23(m23) + ._m30(sx * (m30 - ox * m33) + ox * m33) + ._m31(sy * (m31 - oy * m33) + oy * m33) + ._m32(sz * (m32 - oz * m33) + oz * m33) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | (one ? 0 : PROPERTY_ORTHONORMAL))); + return dest; + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix4d().translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz).mul(this, this) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz) { + return scaleAroundLocal(sx, sy, sz, ox, oy, oz, this); + } + + /** + * Pre-multiply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix4d().translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz).mul(this, this) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz) { + return scaleAroundLocal(factor, factor, factor, ox, oy, oz, this); + } + + public Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz, Matrix4d dest) { + return scaleAroundLocal(factor, factor, factor, ox, oy, oz, dest); + } + + public Matrix4d rotate(double ang, double x, double y, double z, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(ang, x, y, z); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(ang, x, y, z, dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return rotateAffine(ang, x, y, z, dest); + return rotateGeneric(ang, x, y, z, dest); + } + private Matrix4d rotateGeneric(double ang, double x, double y, double z, Matrix4d dest) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotateX(x * ang, dest); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotateY(y * ang, dest); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotateZ(z * ang, dest); + return rotateGenericInternal(ang, x, y, z, dest); + } + private Matrix4d rotateGenericInternal(double ang, double x, double y, double z, Matrix4d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double rm00 = xx * C + c; + double rm01 = xy * C + z * s; + double rm02 = xz * C - y * s; + double rm10 = xy * C - z * s; + double rm11 = yy * C + c; + double rm12 = yz * C + x * s; + double rm20 = xz * C + y * s; + double rm21 = yz * C - x * s; + double rm22 = zz * C + c; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the given axis specified as x, y and z components. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle is in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4d rotate(double ang, double x, double y, double z) { + return rotate(ang, x, y, z, this); + } + + /** + * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateTranslation(double ang, double x, double y, double z, Matrix4d dest) { + double tx = m30, ty = m31, tz = m32; + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return dest.rotationX(x * ang).setTranslation(tx, ty, tz); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return dest.rotationY(y * ang).setTranslation(tx, ty, tz); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return dest.rotationZ(z * ang).setTranslation(tx, ty, tz); + return rotateTranslationInternal(ang, x, y, z, dest); + } + private Matrix4d rotateTranslationInternal(double ang, double x, double y, double z, Matrix4d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double rm00 = xx * C + c; + double rm01 = xy * C + z * s; + double rm02 = xz * C - y * s; + double rm10 = xy * C - z * s; + double rm11 = yy * C + c; + double rm12 = yz * C + x * s; + double rm20 = xz * C + y * s; + double rm21 = yz * C - x * s; + double rm22 = zz * C + c; + double nm00 = rm00; + double nm01 = rm01; + double nm02 = rm02; + double nm10 = rm10; + double nm11 = rm11; + double nm12 = rm12; + // set non-dependent values directly + dest._m20(rm20) + ._m21(rm21) + ._m22(rm22) + // set other values + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateAffine(double ang, double x, double y, double z, Matrix4d dest) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotateX(x * ang, dest); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotateY(y * ang, dest); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotateZ(z * ang, dest); + return rotateAffineInternal(ang, x, y, z, dest); + } + private Matrix4d rotateAffineInternal(double ang, double x, double y, double z, Matrix4d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double rm00 = xx * C + c; + double rm01 = xy * C + z * s; + double rm02 = xz * C - y * s; + double rm10 = xy * C - z * s; + double rm11 = yy * C + c; + double rm12 = yz * C + x * s; + double rm20 = xz * C + y * s; + double rm21 = yz * C - x * s; + double rm22 = zz * C + c; + // add temporaries for dependent values + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + // set non-dependent values directly + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0) + // set other values + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4d rotateAffine(double ang, double x, double y, double z) { + return rotateAffine(ang, x, y, z, this); + } + + /** + * Apply the rotation transformation of the given {@link Quaterniondc} to this matrix while using (ox, oy, oz) as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4d rotateAround(Quaterniondc quat, double ox, double oy, double oz) { + return rotateAround(quat, ox, oy, oz, this); + } + + public Matrix4d rotateAroundAffine(Quaterniondc quat, double ox, double oy, double oz, Matrix4d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = -dzw + dxy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + double tm30 = m00 * ox + m10 * oy + m20 * oz + m30; + double tm31 = m01 * ox + m11 * oy + m21 * oz + m31; + double tm32 = m02 * ox + m12 * oy + m22 * oz + m32; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30) + ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31) + ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32) + ._m33(1.0) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + public Matrix4d rotateAround(Quaterniondc quat, double ox, double oy, double oz, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return rotationAround(quat, ox, oy, oz); + else if ((properties & PROPERTY_AFFINE) != 0) + return rotateAroundAffine(quat, ox, oy, oz, this); + return rotateAroundGeneric(quat, ox, oy, oz, this); + } + private Matrix4d rotateAroundGeneric(Quaterniondc quat, double ox, double oy, double oz, Matrix4d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = -dzw + dxy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + double tm30 = m00 * ox + m10 * oy + m20 * oz + m30; + double tm31 = m01 * ox + m11 * oy + m21 * oz + m31; + double tm32 = m02 * ox + m12 * oy + m22 * oz + m32; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30) + ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31) + ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Set this matrix to a transformation composed of a rotation of the specified {@link Quaterniondc} while using (ox, oy, oz) as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4d rotationAround(Quaterniondc quat, double ox, double oy, double oz) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + this._m20(dyw + dxz); + this._m21(dyz - dxw); + this._m22(z2 - y2 - x2 + w2); + this._m23(0.0); + this._m00(w2 + x2 - z2 - y2); + this._m01(dxy + dzw); + this._m02(dxz - dyw); + this._m03(0.0); + this._m10(-dzw + dxy); + this._m11(y2 - z2 + w2 - x2); + this._m12(dyz + dxw); + this._m13(0.0); + this._m30(-m00 * ox - m10 * oy - m20 * oz + ox); + this._m31(-m01 * ox - m11 * oy - m21 * oz + oy); + this._m32(-m02 * ox - m12 * oy - m22 * oz + oz); + this._m33(1.0); + this.properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateLocal(double ang, double x, double y, double z, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(ang, x, y, z); + return rotateLocalGeneric(ang, x, y, z, dest); + } + private Matrix4d rotateLocalGeneric(double ang, double x, double y, double z, Matrix4d dest) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotateLocalX(x * ang, dest); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotateLocalY(y * ang, dest); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotateLocalZ(z * ang, dest); + return rotateLocalGenericInternal(ang, x, y, z, dest); + } + private Matrix4d rotateLocalGenericInternal(double ang, double x, double y, double z, Matrix4d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double lm00 = xx * C + c; + double lm01 = xy * C + z * s; + double lm02 = xz * C - y * s; + double lm10 = xy * C - z * s; + double lm11 = yy * C + c; + double lm12 = yz * C + x * s; + double lm20 = xz * C + y * s; + double lm21 = yz * C - x * s; + double lm22 = zz * C + c; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(m03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(m13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4d rotateLocal(double ang, double x, double y, double z) { + return rotateLocal(ang, x, y, z, this); + } + + public Matrix4d rotateAroundLocal(Quaterniondc quat, double ox, double oy, double oz, Matrix4d dest) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(); + double xy = quat.x() * quat.y(); + double xz = quat.x() * quat.z(); + double yw = quat.y() * quat.w(); + double yz = quat.y() * quat.z(); + double xw = quat.x() * quat.w(); + double lm00 = w2 + x2 - z2 - y2; + double lm01 = xy + zw + zw + xy; + double lm02 = xz - yw + xz - yw; + double lm10 = -zw + xy - zw + xy; + double lm11 = y2 - z2 + w2 - x2; + double lm12 = yz + yz + xw + xw; + double lm20 = yw + xz + xz + yw; + double lm21 = yz + yz - xw - xw; + double lm22 = z2 - y2 - x2 + w2; + double tm00 = m00 - ox * m03; + double tm01 = m01 - oy * m03; + double tm02 = m02 - oz * m03; + double tm10 = m10 - ox * m13; + double tm11 = m11 - oy * m13; + double tm12 = m12 - oz * m13; + double tm20 = m20 - ox * m23; + double tm21 = m21 - oy * m23; + double tm22 = m22 - oz * m23; + double tm30 = m30 - ox * m33; + double tm31 = m31 - oy * m33; + double tm32 = m32 - oz * m33; + dest._m00(lm00 * tm00 + lm10 * tm01 + lm20 * tm02 + ox * m03) + ._m01(lm01 * tm00 + lm11 * tm01 + lm21 * tm02 + oy * m03) + ._m02(lm02 * tm00 + lm12 * tm01 + lm22 * tm02 + oz * m03) + ._m03(m03) + ._m10(lm00 * tm10 + lm10 * tm11 + lm20 * tm12 + ox * m13) + ._m11(lm01 * tm10 + lm11 * tm11 + lm21 * tm12 + oy * m13) + ._m12(lm02 * tm10 + lm12 * tm11 + lm22 * tm12 + oz * m13) + ._m13(m13) + ._m20(lm00 * tm20 + lm10 * tm21 + lm20 * tm22 + ox * m23) + ._m21(lm01 * tm20 + lm11 * tm21 + lm21 * tm22 + oy * m23) + ._m22(lm02 * tm20 + lm12 * tm21 + lm22 * tm22 + oz * m23) + ._m23(m23) + ._m30(lm00 * tm30 + lm10 * tm31 + lm20 * tm32 + ox * m33) + ._m31(lm01 * tm30 + lm11 * tm31 + lm21 * tm32 + oy * m33) + ._m32(lm02 * tm30 + lm12 * tm31 + lm22 * tm32 + oz * m33) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix while using (ox, oy, oz) + * as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * This method is equivalent to calling: translateLocal(-ox, -oy, -oz).rotateLocal(quat).translateLocal(ox, oy, oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4d rotateAroundLocal(Quaterniondc quat, double ox, double oy, double oz) { + return rotateAroundLocal(quat, ox, oy, oz, this); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3dc)}. + * + * @see #translation(Vector3dc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4d translate(Vector3dc offset) { + return translate(offset.x(), offset.y(), offset.z()); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3dc)}. + * + * @see #translation(Vector3dc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d translate(Vector3dc offset, Matrix4d dest) { + return translate(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4d translate(Vector3fc offset) { + return translate(offset.x(), offset.y(), offset.z()); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d translate(Vector3fc offset, Matrix4d dest) { + return translate(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(double, double, double)}. + * + * @see #translation(double, double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d translate(double x, double y, double z, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.translation(x, y, z); + return translateGeneric(x, y, z, dest); + } + private Matrix4d translateGeneric(double x, double y, double z, Matrix4d dest) { + dest._m00(m00) + ._m01(m01) + ._m02(m02) + ._m03(m03) + ._m10(m10) + ._m11(m11) + ._m12(m12) + ._m13(m13) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30)))) + ._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31)))) + ._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32)))) + ._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)))) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY)); + return dest; + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(double, double, double)}. + * + * @see #translation(double, double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4d translate(double x, double y, double z) { + if ((properties & PROPERTY_IDENTITY) != 0) + return translation(x, y, z); + this._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30)))); + this._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31)))); + this._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32)))); + this._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)))); + this.properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY); + return this; + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4d translateLocal(Vector3fc offset) { + return translateLocal(offset.x(), offset.y(), offset.z()); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d translateLocal(Vector3fc offset, Matrix4d dest) { + return translateLocal(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3dc)}. + * + * @see #translation(Vector3dc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4d translateLocal(Vector3dc offset) { + return translateLocal(offset.x(), offset.y(), offset.z()); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3dc)}. + * + * @see #translation(Vector3dc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d translateLocal(Vector3dc offset, Matrix4d dest) { + return translateLocal(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(double, double, double)}. + * + * @see #translation(double, double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d translateLocal(double x, double y, double z, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.translation(x, y, z); + return translateLocalGeneric(x, y, z, dest); + } + private Matrix4d translateLocalGeneric(double x, double y, double z, Matrix4d dest) { + double nm00 = m00 + x * m03; + double nm01 = m01 + y * m03; + double nm02 = m02 + z * m03; + double nm10 = m10 + x * m13; + double nm11 = m11 + y * m13; + double nm12 = m12 + z * m13; + double nm20 = m20 + x * m23; + double nm21 = m21 + y * m23; + double nm22 = m22 + z * m23; + double nm30 = m30 + x * m33; + double nm31 = m31 + y * m33; + double nm32 = m32 + z * m33; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(m03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(m13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY)); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(double, double, double)}. + * + * @see #translation(double, double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4d translateLocal(double x, double y, double z) { + return translateLocal(x, y, z, this); + } + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(double) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(double) + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateLocalX(double ang, Matrix4d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm02 = sin * m01 + cos * m02; + double nm12 = sin * m11 + cos * m12; + double nm22 = sin * m21 + cos * m22; + double nm32 = sin * m31 + cos * m32; + dest + ._m00(m00) + ._m01(cos * m01 - sin * m02) + ._m02(nm02) + ._m03(m03) + ._m10(m10) + ._m11(cos * m11 - sin * m12) + ._m12(nm12) + ._m13(m13) + ._m20(m20) + ._m21(cos * m21 - sin * m22) + ._m22(nm22) + ._m23(m23) + ._m30(m30) + ._m31(cos * m31 - sin * m32) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(double) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(double) + * + * @param ang + * the angle in radians to rotate about the X axis + * @return this + */ + public Matrix4d rotateLocalX(double ang) { + return rotateLocalX(ang, this); + } + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateLocalY(double ang, Matrix4d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm02 = -sin * m00 + cos * m02; + double nm12 = -sin * m10 + cos * m12; + double nm22 = -sin * m20 + cos * m22; + double nm32 = -sin * m30 + cos * m32; + dest + ._m00(cos * m00 + sin * m02) + ._m01(m01) + ._m02(nm02) + ._m03(m03) + ._m10(cos * m10 + sin * m12) + ._m11(m11) + ._m12(nm12) + ._m13(m13) + ._m20(cos * m20 + sin * m22) + ._m21(m21) + ._m22(nm22) + ._m23(m23) + ._m30(cos * m30 + sin * m32) + ._m31(m31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @return this + */ + public Matrix4d rotateLocalY(double ang) { + return rotateLocalY(ang, this); + } + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(double) rotationZ()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationZ(double) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateLocalZ(double ang, Matrix4d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm01 = sin * m00 + cos * m01; + double nm11 = sin * m10 + cos * m11; + double nm21 = sin * m20 + cos * m21; + double nm31 = sin * m30 + cos * m31; + dest + ._m00(cos * m00 - sin * m01) + ._m01(nm01) + ._m02(m02) + ._m03(m03) + ._m10(cos * m10 - sin * m11) + ._m11(nm11) + ._m12(m12) + ._m13(m13) + ._m20(cos * m20 - sin * m21) + ._m21(nm21) + ._m22(m22) + ._m23(m23) + ._m30(cos * m30 - sin * m31) + ._m31(nm31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @return this + */ + public Matrix4d rotateLocalZ(double ang) { + return rotateLocalZ(ang, this); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(m00); + out.writeDouble(m01); + out.writeDouble(m02); + out.writeDouble(m03); + out.writeDouble(m10); + out.writeDouble(m11); + out.writeDouble(m12); + out.writeDouble(m13); + out.writeDouble(m20); + out.writeDouble(m21); + out.writeDouble(m22); + out.writeDouble(m23); + out.writeDouble(m30); + out.writeDouble(m31); + out.writeDouble(m32); + out.writeDouble(m33); + } + + public void readExternal(ObjectInput in) throws IOException { + _m00(in.readDouble()). + _m01(in.readDouble()). + _m02(in.readDouble()). + _m03(in.readDouble()). + _m10(in.readDouble()). + _m11(in.readDouble()). + _m12(in.readDouble()). + _m13(in.readDouble()). + _m20(in.readDouble()). + _m21(in.readDouble()). + _m22(in.readDouble()). + _m23(in.readDouble()). + _m30(in.readDouble()). + _m31(in.readDouble()). + _m32(in.readDouble()). + _m33(in.readDouble()). + determineProperties(); + } + + public Matrix4d rotateX(double ang, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationX(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double x = m30, y = m31, z = m32; + return dest.rotationX(ang).setTranslation(x, y, z); + } + return rotateXInternal(ang, dest); + } + private Matrix4d rotateXInternal(double ang, Matrix4d dest) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + double rm11 = cos; + double rm12 = sin; + double rm21 = -sin; + double rm22 = cos; + + // add temporaries for dependent values + double nm10 = m10 * rm11 + m20 * rm12; + double nm11 = m11 * rm11 + m21 * rm12; + double nm12 = m12 * rm11 + m22 * rm12; + double nm13 = m13 * rm11 + m23 * rm12; + // set non-dependent values directly + dest._m20(m10 * rm21 + m20 * rm22) + ._m21(m11 * rm21 + m21 * rm22) + ._m22(m12 * rm21 + m22 * rm22) + ._m23(m13 * rm21 + m23 * rm22) + // set other values + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m00(m00) + ._m01(m01) + ._m02(m02) + ._m03(m03) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4d rotateX(double ang) { + return rotateX(ang, this); + } + + public Matrix4d rotateY(double ang, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationY(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double x = m30, y = m31, z = m32; + return dest.rotationY(ang).setTranslation(x, y, z); + } + return rotateYInternal(ang, dest); + } + private Matrix4d rotateYInternal(double ang, Matrix4d dest) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + double rm00 = cos; + double rm02 = -sin; + double rm20 = sin; + double rm22 = cos; + + // add temporaries for dependent values + double nm00 = m00 * rm00 + m20 * rm02; + double nm01 = m01 * rm00 + m21 * rm02; + double nm02 = m02 * rm00 + m22 * rm02; + double nm03 = m03 * rm00 + m23 * rm02; + // set non-dependent values directly + dest._m20(m00 * rm20 + m20 * rm22) + ._m21(m01 * rm20 + m21 * rm22) + ._m22(m02 * rm20 + m22 * rm22) + ._m23(m03 * rm20 + m23 * rm22) + // set other values + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(m10) + ._m11(m11) + ._m12(m12) + ._m13(m13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4d rotateY(double ang) { + return rotateY(ang, this); + } + + public Matrix4d rotateZ(double ang, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationZ(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double x = m30, y = m31, z = m32; + return dest.rotationZ(ang).setTranslation(x, y, z); + } + return rotateZInternal(ang, dest); + } + private Matrix4d rotateZInternal(double ang, Matrix4d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + return rotateTowardsXY(sin, cos, dest); + } + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4d rotateZ(double ang) { + return rotateZ(ang, this); + } + + /** + * Apply rotation about the Z axis to align the local +X towards (dirX, dirY). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * The vector (dirX, dirY) must be a unit vector. + * + * @param dirX + * the x component of the normalized direction + * @param dirY + * the y component of the normalized direction + * @return this + */ + public Matrix4d rotateTowardsXY(double dirX, double dirY) { + return rotateTowardsXY(dirX, dirY, this); + } + + public Matrix4d rotateTowardsXY(double dirX, double dirY, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationTowardsXY(dirX, dirY); + double rm00 = dirY; + double rm01 = dirX; + double rm10 = -dirX; + double rm11 = dirY; + double nm00 = m00 * rm00 + m10 * rm01; + double nm01 = m01 * rm00 + m11 * rm01; + double nm02 = m02 * rm00 + m12 * rm01; + double nm03 = m03 * rm00 + m13 * rm01; + dest._m10(m00 * rm10 + m10 * rm11) + ._m11(m01 * rm10 + m11 * rm11) + ._m12(m02 * rm10 + m12 * rm11) + ._m13(m03 * rm10 + m13 * rm11) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation of angles.x radians about the X axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angles.x).rotateY(angles.y).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4d rotateXYZ(Vector3d angles) { + return rotateXYZ(angles.x, angles.y, angles.z); + } + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4d rotateXYZ(double angleX, double angleY, double angleZ) { + return rotateXYZ(angleX, angleY, angleZ, this); + } + + public Matrix4d rotateXYZ(double angleX, double angleY, double angleZ, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationXYZ(angleX, angleY, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double tx = m30, ty = m31, tz = m32; + return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz); + } else if ((properties & PROPERTY_AFFINE) != 0) + return dest.rotateAffineXYZ(angleX, angleY, angleZ); + return rotateXYZInternal(angleX, angleY, angleZ, dest); + } + private Matrix4d rotateXYZInternal(double angleX, double angleY, double angleZ, Matrix4d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + + // rotateX + double nm10 = m10 * cosX + m20 * sinX; + double nm11 = m11 * cosX + m21 * sinX; + double nm12 = m12 * cosX + m22 * sinX; + double nm13 = m13 * cosX + m23 * sinX; + double nm20 = m10 * m_sinX + m20 * cosX; + double nm21 = m11 * m_sinX + m21 * cosX; + double nm22 = m12 * m_sinX + m22 * cosX; + double nm23 = m13 * m_sinX + m23 * cosX; + // rotateY + double nm00 = m00 * cosY + nm20 * m_sinY; + double nm01 = m01 * cosY + nm21 * m_sinY; + double nm02 = m02 * cosY + nm22 * m_sinY; + double nm03 = m03 * cosY + nm23 * m_sinY; + dest._m20(m00 * sinY + nm20 * cosY) + ._m21(m01 * sinY + nm21 * cosY) + ._m22(m02 * sinY + nm22 * cosY) + ._m23(m03 * sinY + nm23 * cosY) + // rotateZ + ._m00(nm00 * cosZ + nm10 * sinZ) + ._m01(nm01 * cosZ + nm11 * sinZ) + ._m02(nm02 * cosZ + nm12 * sinZ) + ._m03(nm03 * cosZ + nm13 * sinZ) + ._m10(nm00 * m_sinZ + nm10 * cosZ) + ._m11(nm01 * m_sinZ + nm11 * cosZ) + ._m12(nm02 * m_sinZ + nm12 * cosZ) + ._m13(nm03 * m_sinZ + nm13 * cosZ) + // copy last column from 'this' + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ) { + return rotateAffineXYZ(angleX, angleY, angleZ, this); + } + + public Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationXYZ(angleX, angleY, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double tx = m30, ty = m31, tz = m32; + return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz); + } + return rotateAffineXYZInternal(angleX, angleY, angleZ, dest); + } + private Matrix4d rotateAffineXYZInternal(double angleX, double angleY, double angleZ, Matrix4d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + + // rotateX + double nm10 = m10 * cosX + m20 * sinX; + double nm11 = m11 * cosX + m21 * sinX; + double nm12 = m12 * cosX + m22 * sinX; + double nm20 = m10 * m_sinX + m20 * cosX; + double nm21 = m11 * m_sinX + m21 * cosX; + double nm22 = m12 * m_sinX + m22 * cosX; + // rotateY + double nm00 = m00 * cosY + nm20 * m_sinY; + double nm01 = m01 * cosY + nm21 * m_sinY; + double nm02 = m02 * cosY + nm22 * m_sinY; + dest._m20(m00 * sinY + nm20 * cosY) + ._m21(m01 * sinY + nm21 * cosY) + ._m22(m02 * sinY + nm22 * cosY) + ._m23(0.0) + // rotateZ + ._m00(nm00 * cosZ + nm10 * sinZ) + ._m01(nm01 * cosZ + nm11 * sinZ) + ._m02(nm02 * cosZ + nm12 * sinZ) + ._m03(0.0) + ._m10(nm00 * m_sinZ + nm10 * cosZ) + ._m11(nm01 * m_sinZ + nm11 * cosZ) + ._m12(nm02 * m_sinZ + nm12 * cosZ) + ._m13(0.0) + // copy last column from 'this' + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation of angles.z radians about the Z axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.x radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angles.z).rotateY(angles.y).rotateX(angles.x) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4d rotateZYX(Vector3d angles) { + return rotateZYX(angles.z, angles.y, angles.x); + } + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4d rotateZYX(double angleZ, double angleY, double angleX) { + return rotateZYX(angleZ, angleY, angleX, this); + } + + public Matrix4d rotateZYX(double angleZ, double angleY, double angleX, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationZYX(angleZ, angleY, angleX); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double tx = m30, ty = m31, tz = m32; + return dest.rotationZYX(angleZ, angleY, angleX).setTranslation(tx, ty, tz); + } else if ((properties & PROPERTY_AFFINE) != 0) + return dest.rotateAffineZYX(angleZ, angleY, angleX); + return rotateZYXInternal(angleZ, angleY, angleX, dest); + } + private Matrix4d rotateZYXInternal(double angleZ, double angleY, double angleX, Matrix4d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + + // rotateZ + double nm00 = m00 * cosZ + m10 * sinZ; + double nm01 = m01 * cosZ + m11 * sinZ; + double nm02 = m02 * cosZ + m12 * sinZ; + double nm03 = m03 * cosZ + m13 * sinZ; + double nm10 = m00 * m_sinZ + m10 * cosZ; + double nm11 = m01 * m_sinZ + m11 * cosZ; + double nm12 = m02 * m_sinZ + m12 * cosZ; + double nm13 = m03 * m_sinZ + m13 * cosZ; + // rotateY + double nm20 = nm00 * sinY + m20 * cosY; + double nm21 = nm01 * sinY + m21 * cosY; + double nm22 = nm02 * sinY + m22 * cosY; + double nm23 = nm03 * sinY + m23 * cosY; + dest._m00(nm00 * cosY + m20 * m_sinY) + ._m01(nm01 * cosY + m21 * m_sinY) + ._m02(nm02 * cosY + m22 * m_sinY) + ._m03(nm03 * cosY + m23 * m_sinY) + // rotateX + ._m10(nm10 * cosX + nm20 * sinX) + ._m11(nm11 * cosX + nm21 * sinX) + ._m12(nm12 * cosX + nm22 * sinX) + ._m13(nm13 * cosX + nm23 * sinX) + ._m20(nm10 * m_sinX + nm20 * cosX) + ._m21(nm11 * m_sinX + nm21 * cosX) + ._m22(nm12 * m_sinX + nm22 * cosX) + ._m23(nm13 * m_sinX + nm23 * cosX) + // copy last column from 'this' + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX) { + return rotateAffineZYX(angleZ, angleY, angleX, this); + } + + public Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX, Matrix4d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + + // rotateZ + double nm00 = m00 * cosZ + m10 * sinZ; + double nm01 = m01 * cosZ + m11 * sinZ; + double nm02 = m02 * cosZ + m12 * sinZ; + double nm10 = m00 * m_sinZ + m10 * cosZ; + double nm11 = m01 * m_sinZ + m11 * cosZ; + double nm12 = m02 * m_sinZ + m12 * cosZ; + // rotateY + double nm20 = nm00 * sinY + m20 * cosY; + double nm21 = nm01 * sinY + m21 * cosY; + double nm22 = nm02 * sinY + m22 * cosY; + dest._m00(nm00 * cosY + m20 * m_sinY) + ._m01(nm01 * cosY + m21 * m_sinY) + ._m02(nm02 * cosY + m22 * m_sinY) + ._m03(0.0) + // rotateX + ._m10(nm10 * cosX + nm20 * sinX) + ._m11(nm11 * cosX + nm21 * sinX) + ._m12(nm12 * cosX + nm22 * sinX) + ._m13(0.0) + ._m20(nm10 * m_sinX + nm20 * cosX) + ._m21(nm11 * m_sinX + nm21 * cosX) + ._m22(nm12 * m_sinX + nm22 * cosX) + ._m23(0.0) + // copy last column from 'this' + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation of angles.y radians about the Y axis, followed by a rotation of angles.x radians about the X axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4d rotateYXZ(Vector3d angles) { + return rotateYXZ(angles.y, angles.x, angles.z); + } + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4d rotateYXZ(double angleY, double angleX, double angleZ) { + return rotateYXZ(angleY, angleX, angleZ, this); + } + + public Matrix4d rotateYXZ(double angleY, double angleX, double angleZ, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationYXZ(angleY, angleX, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double tx = m30, ty = m31, tz = m32; + return dest.rotationYXZ(angleY, angleX, angleZ).setTranslation(tx, ty, tz); + } else if ((properties & PROPERTY_AFFINE) != 0) + return dest.rotateAffineYXZ(angleY, angleX, angleZ); + return rotateYXZInternal(angleY, angleX, angleZ, dest); + } + private Matrix4d rotateYXZInternal(double angleY, double angleX, double angleZ, Matrix4d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm20 = m00 * sinY + m20 * cosY; + double nm21 = m01 * sinY + m21 * cosY; + double nm22 = m02 * sinY + m22 * cosY; + double nm23 = m03 * sinY + m23 * cosY; + double nm00 = m00 * cosY + m20 * m_sinY; + double nm01 = m01 * cosY + m21 * m_sinY; + double nm02 = m02 * cosY + m22 * m_sinY; + double nm03 = m03 * cosY + m23 * m_sinY; + // rotateX + double nm10 = m10 * cosX + nm20 * sinX; + double nm11 = m11 * cosX + nm21 * sinX; + double nm12 = m12 * cosX + nm22 * sinX; + double nm13 = m13 * cosX + nm23 * sinX; + dest._m20(m10 * m_sinX + nm20 * cosX) + ._m21(m11 * m_sinX + nm21 * cosX) + ._m22(m12 * m_sinX + nm22 * cosX) + ._m23(m13 * m_sinX + nm23 * cosX) + // rotateZ + ._m00(nm00 * cosZ + nm10 * sinZ) + ._m01(nm01 * cosZ + nm11 * sinZ) + ._m02(nm02 * cosZ + nm12 * sinZ) + ._m03(nm03 * cosZ + nm13 * sinZ) + ._m10(nm00 * m_sinZ + nm10 * cosZ) + ._m11(nm01 * m_sinZ + nm11 * cosZ) + ._m12(nm02 * m_sinZ + nm12 * cosZ) + ._m13(nm03 * m_sinZ + nm13 * cosZ) + // copy last column from 'this' + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ) { + return rotateAffineYXZ(angleY, angleX, angleZ, this); + } + + public Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ, Matrix4d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm20 = m00 * sinY + m20 * cosY; + double nm21 = m01 * sinY + m21 * cosY; + double nm22 = m02 * sinY + m22 * cosY; + double nm00 = m00 * cosY + m20 * m_sinY; + double nm01 = m01 * cosY + m21 * m_sinY; + double nm02 = m02 * cosY + m22 * m_sinY; + // rotateX + double nm10 = m10 * cosX + nm20 * sinX; + double nm11 = m11 * cosX + nm21 * sinX; + double nm12 = m12 * cosX + nm22 * sinX; + dest._m20(m10 * m_sinX + nm20 * cosX) + ._m21(m11 * m_sinX + nm21 * cosX) + ._m22(m12 * m_sinX + nm22 * cosX) + ._m23(0.0) + // rotateZ + ._m00(nm00 * cosZ + nm10 * sinZ) + ._m01(nm01 * cosZ + nm11 * sinZ) + ._m02(nm02 * cosZ + nm12 * sinZ) + ._m03(0.0) + ._m10(nm00 * m_sinZ + nm10 * cosZ) + ._m11(nm01 * m_sinZ + nm11 * cosZ) + ._m12(nm02 * m_sinZ + nm12 * cosZ) + ._m13(0.0) + // copy last column from 'this' + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4f}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4f) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4f) + * + * @param angleAxis + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix4d rotation(AxisAngle4f angleAxis) { + return rotation(angleAxis.angle, angleAxis.x, angleAxis.y, angleAxis.z); + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4d}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4d) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4d) + * + * @param angleAxis + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @return this + */ + public Matrix4d rotation(AxisAngle4d angleAxis) { + return rotation(angleAxis.angle, angleAxis.x, angleAxis.y, angleAxis.z); + } + + /** + * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaterniondc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaterniondc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix4d rotation(Quaterniondc quat) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw; + double xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz; + double yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz; + double xw = quat.x() * quat.w(), dxw = xw + xw; + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + _m00(w2 + x2 - z2 - y2). + _m01(dxy + dzw). + _m02(dxz - dyw). + _m10(-dzw + dxy). + _m11(y2 - z2 + w2 - x2). + _m12(dyz + dxw). + _m20(dyw + dxz). + _m21(dyz - dxw). + _m22(z2 - y2 - x2 + w2). + _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaternionfc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaternionfc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4d rotation(Quaternionfc quat) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw; + double xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz; + double yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz; + double xw = quat.x() * quat.w(), dxw = xw + xw; + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + _m00(w2 + x2 - z2 - y2). + _m01(dxy + dzw). + _m02(dxz - dyw). + _m10(-dzw + dxy). + _m11(y2 - z2 + w2 - x2). + _m12(dyz + dxw). + _m20(dyw + dxz). + _m21(dyz - dxw). + _m22(z2 - y2 - x2 + w2). + _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to T * R * S, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), and S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz). + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz) + * + * @see #translation(double, double, double) + * @see #rotate(Quaterniondc) + * @see #scale(double, double, double) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @return this + */ + public Matrix4d translationRotateScale(double tx, double ty, double tz, + double qx, double qy, double qz, double qw, + double sx, double sy, double sz) { + double dqx = qx + qx, dqy = qy + qy, dqz = qz + qz; + double q00 = dqx * qx; + double q11 = dqy * qy; + double q22 = dqz * qz; + double q01 = dqx * qy; + double q02 = dqx * qz; + double q03 = dqx * qw; + double q12 = dqy * qz; + double q13 = dqy * qw; + double q23 = dqz * qw; + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + _m00(sx - (q11 + q22) * sx). + _m01((q01 + q23) * sx). + _m02((q02 - q13) * sx). + _m03(0.0). + _m10((q01 - q23) * sy). + _m11(sy - (q22 + q00) * sy). + _m12((q12 + q03) * sy). + _m13(0.0). + _m20((q02 + q13) * sz). + _m21((q12 - q03) * sz). + _m22(sz - (q11 + q00) * sz). + _m23(0.0). + _m30(tx). + _m31(ty). + _m32(tz). + _m33(1.0). + properties = PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0); + return this; + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaternionfc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4d translationRotateScale(Vector3fc translation, + Quaternionfc quat, + Vector3fc scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3dc) + * @see #rotate(Quaterniondc) + * @see #scale(Vector3dc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4d translationRotateScale(Vector3dc translation, + Quaterniondc quat, + Vector3dc scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to T * R * S, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), and S is a scaling transformation + * which scales all three axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(scale) + * + * @see #translation(double, double, double) + * @see #rotate(Quaterniondc) + * @see #scale(double) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param scale + * the scaling factor for all three axes + * @return this + */ + public Matrix4d translationRotateScale(double tx, double ty, double tz, + double qx, double qy, double qz, double qw, + double scale) { + return translationRotateScale(tx, ty, tz, qx, qy, qz, qw, scale, scale, scale); + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales all three axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3dc) + * @see #rotate(Quaterniondc) + * @see #scale(double) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4d translationRotateScale(Vector3dc translation, + Quaterniondc quat, + double scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale, scale, scale); + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales all three axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaternionfc) + * @see #scale(double) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4d translationRotateScale(Vector3fc translation, + Quaternionfc quat, + double scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale, scale, scale); + } + + /** + * Set this matrix to (T * R * S)-1, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), and S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz). + *

+ * This method is equivalent to calling: translationRotateScale(...).invert() + * + * @see #translationRotateScale(double, double, double, double, double, double, double, double, double, double) + * @see #invert() + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @return this + */ + public Matrix4d translationRotateScaleInvert(double tx, double ty, double tz, + double qx, double qy, double qz, double qw, + double sx, double sy, double sz) { + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + if (one) + return translationRotateScale(tx, ty, tz, qx, qy, qz, qw, sx, sy, sz).invertOrthonormal(this); + double nqx = -qx, nqy = -qy, nqz = -qz; + double dqx = nqx + nqx; + double dqy = nqy + nqy; + double dqz = nqz + nqz; + double q00 = dqx * nqx; + double q11 = dqy * nqy; + double q22 = dqz * nqz; + double q01 = dqx * nqy; + double q02 = dqx * nqz; + double q03 = dqx * qw; + double q12 = dqy * nqz; + double q13 = dqy * qw; + double q23 = dqz * qw; + double isx = 1/sx, isy = 1/sy, isz = 1/sz; + _m00(isx * (1.0 - q11 - q22)). + _m01(isy * (q01 + q23)). + _m02(isz * (q02 - q13)). + _m03(0.0). + _m10(isx * (q01 - q23)). + _m11(isy * (1.0 - q22 - q00)). + _m12(isz * (q12 + q03)). + _m13(0.0). + _m20(isx * (q02 + q13)). + _m21(isy * (q12 - q03)). + _m22(isz * (1.0 - q11 - q00)). + _m23(0.0). + _m30(-m00 * tx - m10 * ty - m20 * tz). + _m31(-m01 * tx - m11 * ty - m21 * tz). + _m32(-m02 * tx - m12 * ty - m22 * tz). + _m33(1.0). + properties = PROPERTY_AFFINE; + return this; + } + + /** + * Set this matrix to (T * R * S)-1, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * This method is equivalent to calling: translationRotateScale(...).invert() + * + * @see #translationRotateScale(Vector3dc, Quaterniondc, Vector3dc) + * @see #invert() + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4d translationRotateScaleInvert(Vector3dc translation, + Quaterniondc quat, + Vector3dc scale) { + return translationRotateScaleInvert(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to (T * R * S)-1, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * This method is equivalent to calling: translationRotateScale(...).invert() + * + * @see #translationRotateScale(Vector3fc, Quaternionfc, Vector3fc) + * @see #invert() + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4d translationRotateScaleInvert(Vector3fc translation, + Quaternionfc quat, + Vector3fc scale) { + return translationRotateScaleInvert(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to (T * R * S)-1, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales all three axes by scale. + *

+ * This method is equivalent to calling: translationRotateScale(...).invert() + * + * @see #translationRotateScale(Vector3dc, Quaterniondc, double) + * @see #invert() + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4d translationRotateScaleInvert(Vector3dc translation, + Quaterniondc quat, + double scale) { + return translationRotateScaleInvert(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale, scale, scale); + } + + /** + * Set this matrix to (T * R * S)-1, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales all three axes by scale. + *

+ * This method is equivalent to calling: translationRotateScale(...).invert() + * + * @see #translationRotateScale(Vector3fc, Quaternionfc, double) + * @see #invert() + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4d translationRotateScaleInvert(Vector3fc translation, + Quaternionfc quat, + double scale) { + return translationRotateScaleInvert(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale, scale, scale); + } + + /** + * Set this matrix to T * R * S * M, where T is a translation by the given (tx, ty, tz), + * R is a rotation - and possibly scaling - transformation specified by the quaternion (qx, qy, qz, qw), S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz) and M is an {@link #isAffine() affine} matrix. + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz).mulAffine(m) + * + * @see #translation(double, double, double) + * @see #rotate(Quaterniondc) + * @see #scale(double, double, double) + * @see #mulAffine(Matrix4dc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @param m + * the {@link #isAffine() affine} matrix to multiply by + * @return this + */ + public Matrix4d translationRotateScaleMulAffine(double tx, double ty, double tz, + double qx, double qy, double qz, double qw, + double sx, double sy, double sz, + Matrix4d m) { + double w2 = qw * qw; + double x2 = qx * qx; + double y2 = qy * qy; + double z2 = qz * qz; + double zw = qz * qw; + double xy = qx * qy; + double xz = qx * qz; + double yw = qy * qw; + double yz = qy * qz; + double xw = qx * qw; + double nm00 = w2 + x2 - z2 - y2; + double nm01 = xy + zw + zw + xy; + double nm02 = xz - yw + xz - yw; + double nm10 = -zw + xy - zw + xy; + double nm11 = y2 - z2 + w2 - x2; + double nm12 = yz + yz + xw + xw; + double nm20 = yw + xz + xz + yw; + double nm21 = yz + yz - xw - xw; + double nm22 = z2 - y2 - x2 + w2; + double m00 = nm00 * m.m00 + nm10 * m.m01 + nm20 * m.m02; + double m01 = nm01 * m.m00 + nm11 * m.m01 + nm21 * m.m02; + this.m02 = nm02 * m.m00 + nm12 * m.m01 + nm22 * m.m02; + this.m00 = m00; + this.m01 = m01; + this.m03 = 0.0; + double m10 = nm00 * m.m10 + nm10 * m.m11 + nm20 * m.m12; + double m11 = nm01 * m.m10 + nm11 * m.m11 + nm21 * m.m12; + this.m12 = nm02 * m.m10 + nm12 * m.m11 + nm22 * m.m12; + this.m10 = m10; + this.m11 = m11; + this.m13 = 0.0; + double m20 = nm00 * m.m20 + nm10 * m.m21 + nm20 * m.m22; + double m21 = nm01 * m.m20 + nm11 * m.m21 + nm21 * m.m22; + this.m22 = nm02 * m.m20 + nm12 * m.m21 + nm22 * m.m22; + this.m20 = m20; + this.m21 = m21; + this.m23 = 0.0; + double m30 = nm00 * m.m30 + nm10 * m.m31 + nm20 * m.m32 + tx; + double m31 = nm01 * m.m30 + nm11 * m.m31 + nm21 * m.m32 + ty; + this.m32 = nm02 * m.m30 + nm12 * m.m31 + nm22 * m.m32 + tz; + this.m30 = m30; + this.m31 = m31; + this.m33 = 1.0; + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + properties = PROPERTY_AFFINE | (one && (m.properties & PROPERTY_ORTHONORMAL) != 0 ? PROPERTY_ORTHONORMAL : 0); + return this; + } + + /** + * Set this matrix to T * R * S * M, where T is the given translation, + * R is a rotation - and possibly scaling - transformation specified by the given quaternion, S is a scaling transformation + * which scales the axes by scale and M is an {@link #isAffine() affine} matrix. + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale).mulAffine(m) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaterniondc) + * @see #mulAffine(Matrix4dc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @param m + * the {@link #isAffine() affine} matrix to multiply by + * @return this + */ + public Matrix4d translationRotateScaleMulAffine(Vector3fc translation, + Quaterniondc quat, + Vector3fc scale, + Matrix4d m) { + return translationRotateScaleMulAffine(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z(), m); + } + + /** + * Set this matrix to T * R, where T is a translation by the given (tx, ty, tz) and + * R is a rotation - and possibly scaling - transformation specified by the quaternion (qx, qy, qz, qw). + *

+ * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat) + * + * @see #translation(double, double, double) + * @see #rotate(Quaterniondc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @return this + */ + public Matrix4d translationRotate(double tx, double ty, double tz, double qx, double qy, double qz, double qw) { + double w2 = qw * qw; + double x2 = qx * qx; + double y2 = qy * qy; + double z2 = qz * qz; + double zw = qz * qw; + double xy = qx * qy; + double xz = qx * qz; + double yw = qy * qw; + double yz = qy * qz; + double xw = qx * qw; + this.m00 = w2 + x2 - z2 - y2; + this.m01 = xy + zw + zw + xy; + this.m02 = xz - yw + xz - yw; + this.m10 = -zw + xy - zw + xy; + this.m11 = y2 - z2 + w2 - x2; + this.m12 = yz + yz + xw + xw; + this.m20 = yw + xz + xz + yw; + this.m21 = yz + yz - xw - xw; + this.m22 = z2 - y2 - x2 + w2; + this.m30 = tx; + this.m31 = ty; + this.m32 = tz; + this.m33 = 1.0; + this.properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to T * R, where T is a translation by the given (tx, ty, tz) and + * R is a rotation - and possibly scaling - transformation specified by the given quaternion. + *

+ * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat) + * + * @see #translation(double, double, double) + * @see #rotate(Quaterniondc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param quat + * the quaternion representing a rotation + * @return this + */ + public Matrix4d translationRotate(double tx, double ty, double tz, Quaterniondc quat) { + return translationRotate(tx, ty, tz, quat.x(), quat.y(), quat.z(), quat.w()); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotate(Quaterniondc quat, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(quat); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(quat, dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return rotateAffine(quat, dest); + return rotateGeneric(quat, dest); + } + private Matrix4d rotateGeneric(Quaterniondc quat, Matrix4d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = -dzw + dxy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotate(Quaternionfc quat, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(quat); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(quat, dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return rotateAffine(quat, dest); + return rotateGeneric(quat, dest); + } + private Matrix4d rotateGeneric(Quaternionfc quat, Matrix4d dest) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(); + double xy = quat.x() * quat.y(); + double xz = quat.x() * quat.z(); + double yw = quat.y() * quat.w(); + double yz = quat.y() * quat.z(); + double xw = quat.x() * quat.w(); + double rm00 = w2 + x2 - z2 - y2; + double rm01 = xy + zw + zw + xy; + double rm02 = xz - yw + xz - yw; + double rm10 = -zw + xy - zw + xy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = yz + yz + xw + xw; + double rm20 = yw + xz + xz + yw; + double rm21 = yz + yz - xw - xw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix4d rotate(Quaterniondc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4d rotate(Quaternionfc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this {@link #isAffine() affine} matrix and store + * the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateAffine(Quaterniondc quat, Matrix4d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = -dzw + dxy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix4d rotateAffine(Quaterniondc quat) { + return rotateAffine(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateTranslation(Quaterniondc quat, Matrix4d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = -dzw + dxy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + dest._m20(rm20) + ._m21(rm21) + ._m22(rm22) + ._m23(0.0) + ._m00(rm00) + ._m01(rm01) + ._m02(rm02) + ._m03(0.0) + ._m10(rm10) + ._m11(rm11) + ._m12(rm12) + ._m13(0.0) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(1.0) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateTranslation(Quaternionfc quat, Matrix4d dest) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(); + double xy = quat.x() * quat.y(); + double xz = quat.x() * quat.z(); + double yw = quat.y() * quat.w(); + double yz = quat.y() * quat.z(); + double xw = quat.x() * quat.w(); + double rm00 = w2 + x2 - z2 - y2; + double rm01 = xy + zw + zw + xy; + double rm02 = xz - yw + xz - yw; + double rm10 = -zw + xy - zw + xy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = yz + yz + xw + xw; + double rm20 = yw + xz + xz + yw; + double rm21 = yz + yz - xw - xw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = rm00; + double nm01 = rm01; + double nm02 = rm02; + double nm10 = rm10; + double nm11 = rm11; + double nm12 = rm12; + dest._m20(rm20) + ._m21(rm21) + ._m22(rm22) + ._m23(0.0) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(1.0) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateLocal(Quaterniondc quat, Matrix4d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double lm00 = w2 + x2 - z2 - y2; + double lm01 = dxy + dzw; + double lm02 = dxz - dyw; + double lm10 = -dzw + dxy; + double lm11 = y2 - z2 + w2 - x2; + double lm12 = dyz + dxw; + double lm20 = dyw + dxz; + double lm21 = dyz - dxw; + double lm22 = z2 - y2 - x2 + w2; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm03 = m03; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm13 = m13; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + double nm23 = m23; + double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix4d rotateLocal(Quaterniondc quat) { + return rotateLocal(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this {@link #isAffine() affine} matrix and store + * the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateAffine(Quaternionfc quat, Matrix4d dest) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(); + double xy = quat.x() * quat.y(); + double xz = quat.x() * quat.z(); + double yw = quat.y() * quat.w(); + double yz = quat.y() * quat.z(); + double xw = quat.x() * quat.w(); + double rm00 = w2 + x2 - z2 - y2; + double rm01 = xy + zw + zw + xy; + double rm02 = xz - yw + xz - yw; + double rm10 = -zw + xy - zw + xy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = yz + yz + xw + xw; + double rm20 = yw + xz + xz + yw; + double rm21 = yz + yz - xw - xw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4d rotateAffine(Quaternionfc quat) { + return rotateAffine(quat, this); + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateLocal(Quaternionfc quat, Matrix4d dest) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(); + double xy = quat.x() * quat.y(); + double xz = quat.x() * quat.z(); + double yw = quat.y() * quat.w(); + double yz = quat.y() * quat.z(); + double xw = quat.x() * quat.w(); + double lm00 = w2 + x2 - z2 - y2; + double lm01 = xy + zw + zw + xy; + double lm02 = xz - yw + xz - yw; + double lm10 = -zw + xy - zw + xy; + double lm11 = y2 - z2 + w2 - x2; + double lm12 = yz + yz + xw + xw; + double lm20 = yw + xz + xz + yw; + double lm21 = yz + yz - xw - xw; + double lm22 = z2 - y2 - x2 + w2; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm03 = m03; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm13 = m13; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + double nm23 = m23; + double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4d rotateLocal(Quaternionfc quat) { + return rotateLocal(quat, this); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f}, to this matrix. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix4d rotate(AxisAngle4f axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotate(AxisAngle4f axisAngle, Matrix4d dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d}, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4d)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @return this + */ + public Matrix4d rotate(AxisAngle4d axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4d)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotate(AxisAngle4d axisAngle, Matrix4d dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3dc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3dc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @return this + */ + public Matrix4d rotate(double angle, Vector3dc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3dc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3dc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotate(double angle, Vector3dc axis, Matrix4d dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix4d rotate(double angle, Vector3fc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotate(double angle, Vector3fc axis, Matrix4d dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + public Vector4d getRow(int row, Vector4d dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + dest.x = m00; + dest.y = m10; + dest.z = m20; + dest.w = m30; + break; + case 1: + dest.x = m01; + dest.y = m11; + dest.z = m21; + dest.w = m31; + break; + case 2: + dest.x = m02; + dest.y = m12; + dest.z = m22; + dest.w = m32; + break; + case 3: + dest.x = m03; + dest.y = m13; + dest.z = m23; + dest.w = m33; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + public Vector3d getRow(int row, Vector3d dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + dest.x = m00; + dest.y = m10; + dest.z = m20; + break; + case 1: + dest.x = m01; + dest.y = m11; + dest.z = m21; + break; + case 2: + dest.x = m02; + dest.y = m12; + dest.z = m22; + break; + case 3: + dest.x = m03; + dest.y = m13; + dest.z = m23; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..3] + * @param src + * the row components to set + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..3] + */ + public Matrix4d setRow(int row, Vector4dc src) throws IndexOutOfBoundsException { + switch (row) { + case 0: + return _m00(src.x())._m10(src.y())._m20(src.z())._m30(src.w())._properties(0); + case 1: + return _m01(src.x())._m11(src.y())._m21(src.z())._m31(src.w())._properties(0); + case 2: + return _m02(src.x())._m12(src.y())._m22(src.z())._m32(src.w())._properties(0); + case 3: + return _m03(src.x())._m13(src.y())._m23(src.z())._m33(src.w())._properties(0); + default: + throw new IndexOutOfBoundsException(); + } + } + + public Vector4d getColumn(int column, Vector4d dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + dest.x = m00; + dest.y = m01; + dest.z = m02; + dest.w = m03; + break; + case 1: + dest.x = m10; + dest.y = m11; + dest.z = m12; + dest.w = m13; + break; + case 2: + dest.x = m20; + dest.y = m21; + dest.z = m22; + dest.w = m23; + break; + case 3: + dest.x = m30; + dest.y = m31; + dest.z = m32; + dest.w = m33; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + public Vector3d getColumn(int column, Vector3d dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + dest.x = m00; + dest.y = m01; + dest.z = m02; + break; + case 1: + dest.x = m10; + dest.y = m11; + dest.z = m12; + break; + case 2: + dest.x = m20; + dest.y = m21; + dest.z = m22; + break; + case 3: + dest.x = m30; + dest.y = m31; + dest.z = m32; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param src + * the column components to set + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + public Matrix4d setColumn(int column, Vector4dc src) throws IndexOutOfBoundsException { + switch (column) { + case 0: + return _m00(src.x())._m01(src.y())._m02(src.z())._m03(src.w())._properties(0); + case 1: + return _m10(src.x())._m11(src.y())._m12(src.z())._m13(src.w())._properties(0); + case 2: + return _m20(src.x())._m21(src.y())._m22(src.z())._m23(src.w())._properties(0); + case 3: + return _m30(src.x())._m31(src.y())._m32(src.z())._m33(src.w())._properties(0); + default: + throw new IndexOutOfBoundsException(); + } + } + + public double get(int column, int row) { + return MemUtil.INSTANCE.get(this, column, row); + } + + /** + * Set the matrix element at the given column and row to the specified value. + * + * @param column + * the colum index in [0..3] + * @param row + * the row index in [0..3] + * @param value + * the value + * @return this + */ + public Matrix4d set(int column, int row, double value) { + return MemUtil.INSTANCE.set(this, column, row, value); + } + + public double getRowColumn(int row, int column) { + return MemUtil.INSTANCE.get(this, column, row); + } + + /** + * Set the matrix element at the given row and column to the specified value. + * + * @param row + * the row index in [0..3] + * @param column + * the colum index in [0..3] + * @param value + * the value + * @return this + */ + public Matrix4d setRowColumn(int row, int column, double value) { + return MemUtil.INSTANCE.set(this, column, row, value); + } + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into the upper left 3x3 submatrix of this. + * All other values of this will be set to {@link #identity() identity}. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link #set3x3(Matrix4dc)} to set a given Matrix4f to only the upper left 3x3 submatrix + * of this matrix. + * + * @see #set3x3(Matrix4dc) + * + * @return this + */ + public Matrix4d normal() { + return normal(this); + } + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into the upper left 3x3 submatrix of dest. + * All other values of dest will be set to {@link #identity() identity}. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link #set3x3(Matrix4dc)} to set a given Matrix4d to only the upper left 3x3 submatrix + * of a given matrix. + * + * @see #set3x3(Matrix4dc) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d normal(Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalOrthonormal(dest); + return normalGeneric(dest); + } + private Matrix4d normalOrthonormal(Matrix4d dest) { + if (dest != this) + dest.set(this); + return dest._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + private Matrix4d normalGeneric(Matrix4d dest) { + double m00m11 = m00 * m11; + double m01m10 = m01 * m10; + double m02m10 = m02 * m10; + double m00m12 = m00 * m12; + double m01m12 = m01 * m12; + double m02m11 = m02 * m11; + double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + double s = 1.0 / det; + /* Invert and transpose in one go */ + double nm00 = (m11 * m22 - m21 * m12) * s; + double nm01 = (m20 * m12 - m10 * m22) * s; + double nm02 = (m10 * m21 - m20 * m11) * s; + double nm10 = (m21 * m02 - m01 * m22) * s; + double nm11 = (m00 * m22 - m20 * m02) * s; + double nm12 = (m20 * m01 - m00 * m21) * s; + double nm20 = (m01m12 - m02m11) * s; + double nm21 = (m02m10 - m00m12) * s; + double nm22 = (m00m11 - m01m10) * s; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(0.0) + ._m30(0.0) + ._m31(0.0) + ._m32(0.0) + ._m33(1.0) + ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE)); + } + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link Matrix3d#set(Matrix4dc)} to set a given Matrix3d to only the upper left 3x3 submatrix + * of this matrix. + * + * @see Matrix3d#set(Matrix4dc) + * @see #get3x3(Matrix3d) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d normal(Matrix3d dest) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalOrthonormal(dest); + return normalGeneric(dest); + } + private Matrix3d normalOrthonormal(Matrix3d dest) { + dest.set(this); + return dest; + } + private Matrix3d normalGeneric(Matrix3d dest) { + double m00m11 = m00 * m11; + double m01m10 = m01 * m10; + double m02m10 = m02 * m10; + double m00m12 = m00 * m12; + double m01m12 = m01 * m12; + double m02m11 = m02 * m11; + double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + double s = 1.0 / det; + /* Invert and transpose in one go */ + return dest._m00((m11 * m22 - m21 * m12) * s) + ._m01((m20 * m12 - m10 * m22) * s) + ._m02((m10 * m21 - m20 * m11) * s) + ._m10((m21 * m02 - m01 * m22) * s) + ._m11((m00 * m22 - m20 * m02) * s) + ._m12((m20 * m01 - m00 * m21) * s) + ._m20((m01m12 - m02m11) * s) + ._m21((m02m10 - m00m12) * s) + ._m22((m00m11 - m01m10) * s); + } + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this. + *

+ * The cofactor matrix can be used instead of {@link #normal()} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @return this + */ + public Matrix4d cofactor3x3() { + return cofactor3x3(this); + } + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this + * and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d cofactor3x3(Matrix3d dest) { + return dest._m00(m11 * m22 - m21 * m12) + ._m01(m20 * m12 - m10 * m22) + ._m02(m10 * m21 - m20 * m11) + ._m10(m21 * m02 - m01 * m22) + ._m11(m00 * m22 - m20 * m02) + ._m12(m20 * m01 - m00 * m21) + ._m20(m01 * m12 - m02 * m11) + ._m21(m02 * m10 - m00 * m12) + ._m22(m00 * m11 - m01 * m10); + } + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this + * and store it into dest. + * All other values of dest will be set to {@link #identity() identity}. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix4d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d cofactor3x3(Matrix4d dest) { + double nm10 = m21 * m02 - m01 * m22; + double nm11 = m00 * m22 - m20 * m02; + double nm12 = m20 * m01 - m00 * m21; + double nm20 = m01 * m12 - m11 * m02; + double nm21 = m02 * m10 - m12 * m00; + double nm22 = m00 * m11 - m10 * m01; + return dest + ._m00(m11 * m22 - m21 * m12) + ._m01(m20 * m12 - m10 * m22) + ._m02(m10 * m21 - m20 * m11) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(0.0) + ._m30(0.0) + ._m31(0.0) + ._m32(0.0) + ._m33(1.0) + ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE)); + } + + /** + * Normalize the upper left 3x3 submatrix of this matrix. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @return this + */ + public Matrix4d normalize3x3() { + return normalize3x3(this); + } + + public Matrix4d normalize3x3(Matrix4d dest) { + double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + dest._m00(m00 * invXlen)._m01(m01 * invXlen)._m02(m02 * invXlen) + ._m10(m10 * invYlen)._m11(m11 * invYlen)._m12(m12 * invYlen) + ._m20(m20 * invZlen)._m21(m21 * invZlen)._m22(m22 * invZlen) + ._m30(m30)._m31(m31)._m32(m32)._m33(m33) + ._properties(properties); + return dest; + } + + public Matrix3d normalize3x3(Matrix3d dest) { + double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + dest.m00(m00 * invXlen); dest.m01(m01 * invXlen); dest.m02(m02 * invXlen); + dest.m10(m10 * invYlen); dest.m11(m11 * invYlen); dest.m12(m12 * invYlen); + dest.m20(m20 * invZlen); dest.m21(m21 * invZlen); dest.m22(m22 * invZlen); + return dest; + } + + public Vector4d unproject(double winX, double winY, double winZ, int[] viewport, Vector4d dest) { + double a = m00 * m11 - m01 * m10; + double b = m00 * m12 - m02 * m10; + double c = m00 * m13 - m03 * m10; + double d = m01 * m12 - m02 * m11; + double e = m01 * m13 - m03 * m11; + double f = m02 * m13 - m03 * m12; + double g = m20 * m31 - m21 * m30; + double h = m20 * m32 - m22 * m30; + double i = m20 * m33 - m23 * m30; + double j = m21 * m32 - m22 * m31; + double k = m21 * m33 - m23 * m31; + double l = m22 * m33 - m23 * m32; + double det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0 / det; + double im00 = ( m11 * l - m12 * k + m13 * j) * det; + double im01 = (-m01 * l + m02 * k - m03 * j) * det; + double im02 = ( m31 * f - m32 * e + m33 * d) * det; + double im03 = (-m21 * f + m22 * e - m23 * d) * det; + double im10 = (-m10 * l + m12 * i - m13 * h) * det; + double im11 = ( m00 * l - m02 * i + m03 * h) * det; + double im12 = (-m30 * f + m32 * c - m33 * b) * det; + double im13 = ( m20 * f - m22 * c + m23 * b) * det; + double im20 = ( m10 * k - m11 * i + m13 * g) * det; + double im21 = (-m00 * k + m01 * i - m03 * g) * det; + double im22 = ( m30 * e - m31 * c + m33 * a) * det; + double im23 = (-m20 * e + m21 * c - m23 * a) * det; + double im30 = (-m10 * j + m11 * h - m12 * g) * det; + double im31 = ( m00 * j - m01 * h + m02 * g) * det; + double im32 = (-m30 * d + m31 * b - m32 * a) * det; + double im33 = ( m20 * d - m21 * b + m22 * a) * det; + double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0; + double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0; + double ndcZ = winZ+winZ-1.0; + double invW = 1.0 / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33); + dest.x = (im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW; + dest.y = (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW; + dest.z = (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW; + dest.w = 1.0; + return dest; + } + + public Vector3d unproject(double winX, double winY, double winZ, int[] viewport, Vector3d dest) { + double a = m00 * m11 - m01 * m10; + double b = m00 * m12 - m02 * m10; + double c = m00 * m13 - m03 * m10; + double d = m01 * m12 - m02 * m11; + double e = m01 * m13 - m03 * m11; + double f = m02 * m13 - m03 * m12; + double g = m20 * m31 - m21 * m30; + double h = m20 * m32 - m22 * m30; + double i = m20 * m33 - m23 * m30; + double j = m21 * m32 - m22 * m31; + double k = m21 * m33 - m23 * m31; + double l = m22 * m33 - m23 * m32; + double det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0 / det; + double im00 = ( m11 * l - m12 * k + m13 * j) * det; + double im01 = (-m01 * l + m02 * k - m03 * j) * det; + double im02 = ( m31 * f - m32 * e + m33 * d) * det; + double im03 = (-m21 * f + m22 * e - m23 * d) * det; + double im10 = (-m10 * l + m12 * i - m13 * h) * det; + double im11 = ( m00 * l - m02 * i + m03 * h) * det; + double im12 = (-m30 * f + m32 * c - m33 * b) * det; + double im13 = ( m20 * f - m22 * c + m23 * b) * det; + double im20 = ( m10 * k - m11 * i + m13 * g) * det; + double im21 = (-m00 * k + m01 * i - m03 * g) * det; + double im22 = ( m30 * e - m31 * c + m33 * a) * det; + double im23 = (-m20 * e + m21 * c - m23 * a) * det; + double im30 = (-m10 * j + m11 * h - m12 * g) * det; + double im31 = ( m00 * j - m01 * h + m02 * g) * det; + double im32 = (-m30 * d + m31 * b - m32 * a) * det; + double im33 = ( m20 * d - m21 * b + m22 * a) * det; + double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0; + double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0; + double ndcZ = winZ+winZ-1.0; + double invW = 1.0 / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33); + dest.x = (im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW; + dest.y = (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW; + dest.z = (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW; + return dest; + } + + public Vector4d unproject(Vector3dc winCoords, int[] viewport, Vector4d dest) { + return unproject(winCoords.x(), winCoords.y(), winCoords.z(), viewport, dest); + } + + public Vector3d unproject(Vector3dc winCoords, int[] viewport, Vector3d dest) { + return unproject(winCoords.x(), winCoords.y(), winCoords.z(), viewport, dest); + } + + public Matrix4d unprojectRay(double winX, double winY, int[] viewport, Vector3d originDest, Vector3d dirDest) { + double a = m00 * m11 - m01 * m10; + double b = m00 * m12 - m02 * m10; + double c = m00 * m13 - m03 * m10; + double d = m01 * m12 - m02 * m11; + double e = m01 * m13 - m03 * m11; + double f = m02 * m13 - m03 * m12; + double g = m20 * m31 - m21 * m30; + double h = m20 * m32 - m22 * m30; + double i = m20 * m33 - m23 * m30; + double j = m21 * m32 - m22 * m31; + double k = m21 * m33 - m23 * m31; + double l = m22 * m33 - m23 * m32; + double det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0 / det; + double im00 = ( m11 * l - m12 * k + m13 * j) * det; + double im01 = (-m01 * l + m02 * k - m03 * j) * det; + double im02 = ( m31 * f - m32 * e + m33 * d) * det; + double im03 = (-m21 * f + m22 * e - m23 * d) * det; + double im10 = (-m10 * l + m12 * i - m13 * h) * det; + double im11 = ( m00 * l - m02 * i + m03 * h) * det; + double im12 = (-m30 * f + m32 * c - m33 * b) * det; + double im13 = ( m20 * f - m22 * c + m23 * b) * det; + double im20 = ( m10 * k - m11 * i + m13 * g) * det; + double im21 = (-m00 * k + m01 * i - m03 * g) * det; + double im22 = ( m30 * e - m31 * c + m33 * a) * det; + double im23 = (-m20 * e + m21 * c - m23 * a) * det; + double im30 = (-m10 * j + m11 * h - m12 * g) * det; + double im31 = ( m00 * j - m01 * h + m02 * g) * det; + double im32 = (-m30 * d + m31 * b - m32 * a) * det; + double im33 = ( m20 * d - m21 * b + m22 * a) * det; + double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0; + double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0; + double px = im00 * ndcX + im10 * ndcY + im30; + double py = im01 * ndcX + im11 * ndcY + im31; + double pz = im02 * ndcX + im12 * ndcY + im32; + double invNearW = 1.0 / (im03 * ndcX + im13 * ndcY - im23 + im33); + double nearX = (px - im20) * invNearW; + double nearY = (py - im21) * invNearW; + double nearZ = (pz - im22) * invNearW; + double invW0 = 1.0 / (im03 * ndcX + im13 * ndcY + im33); + double x0 = px * invW0; + double y0 = py * invW0; + double z0 = pz * invW0; + originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ; + dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ; + return this; + } + + public Matrix4d unprojectRay(Vector2dc winCoords, int[] viewport, Vector3d originDest, Vector3d dirDest) { + return unprojectRay(winCoords.x(), winCoords.y(), viewport, originDest, dirDest); + } + + public Vector4d unprojectInv(Vector3dc winCoords, int[] viewport, Vector4d dest) { + return unprojectInv(winCoords.x(), winCoords.y(), winCoords.z(), viewport, dest); + } + + public Vector4d unprojectInv(double winX, double winY, double winZ, int[] viewport, Vector4d dest) { + double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0; + double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0; + double ndcZ = winZ+winZ-1.0; + double invW = 1.0 / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33); + dest.x = (m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW; + dest.y = (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW; + dest.z = (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW; + dest.w = 1.0; + return dest; + } + + public Vector3d unprojectInv(Vector3dc winCoords, int[] viewport, Vector3d dest) { + return unprojectInv(winCoords.x(), winCoords.y(), winCoords.z(), viewport, dest); + } + + public Vector3d unprojectInv(double winX, double winY, double winZ, int[] viewport, Vector3d dest) { + double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0; + double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0; + double ndcZ = winZ+winZ-1.0; + double invW = 1.0 / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33); + dest.x = (m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW; + dest.y = (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW; + dest.z = (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW; + return dest; + } + + public Matrix4d unprojectInvRay(Vector2dc winCoords, int[] viewport, Vector3d originDest, Vector3d dirDest) { + return unprojectInvRay(winCoords.x(), winCoords.y(), viewport, originDest, dirDest); + } + + public Matrix4d unprojectInvRay(double winX, double winY, int[] viewport, Vector3d originDest, Vector3d dirDest) { + double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0; + double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0; + double px = m00 * ndcX + m10 * ndcY + m30; + double py = m01 * ndcX + m11 * ndcY + m31; + double pz = m02 * ndcX + m12 * ndcY + m32; + double invNearW = 1.0 / (m03 * ndcX + m13 * ndcY - m23 + m33); + double nearX = (px - m20) * invNearW; + double nearY = (py - m21) * invNearW; + double nearZ = (pz - m22) * invNearW; + double invW0 = 1.0 / (m03 * ndcX + m13 * ndcY + m33); + double x0 = px * invW0; + double y0 = py * invW0; + double z0 = pz * invW0; + originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ; + dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ; + return this; + } + + public Vector4d project(double x, double y, double z, int[] viewport, Vector4d winCoordsDest) { + double invW = 1.0 / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))); + double nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW; + double ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW; + double nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW; + return winCoordsDest.set(Math.fma(Math.fma(nx, 0.5, 0.5), viewport[2], viewport[0]), + Math.fma(Math.fma(ny, 0.5, 0.5), viewport[3], viewport[1]), + Math.fma(0.5, nz, 0.5), + 1.0); + } + + public Vector3d project(double x, double y, double z, int[] viewport, Vector3d winCoordsDest) { + double invW = 1.0 / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))); + double nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW; + double ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW; + double nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW; + winCoordsDest.x = Math.fma(Math.fma(nx, 0.5, 0.5), viewport[2], viewport[0]); + winCoordsDest.y = Math.fma(Math.fma(ny, 0.5, 0.5), viewport[3], viewport[1]); + winCoordsDest.z = Math.fma(0.5, nz, 0.5); + return winCoordsDest; + } + + public Vector4d project(Vector3dc position, int[] viewport, Vector4d dest) { + return project(position.x(), position.y(), position.z(), viewport, dest); + } + + public Vector3d project(Vector3dc position, int[] viewport, Vector3d dest) { + return project(position.x(), position.y(), position.z(), viewport, dest); + } + + public Matrix4d reflect(double a, double b, double c, double d, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.reflection(a, b, c, d); + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.reflection(a, b, c, d); + else if ((properties & PROPERTY_AFFINE) != 0) + return reflectAffine(a, b, c, d, dest); + return reflectGeneric(a, b, c, d, dest); + } + private Matrix4d reflectAffine(double a, double b, double c, double d, Matrix4d dest) { + double da = a + a, db = b + b, dc = c + c, dd = d + d; + double rm00 = 1.0 - da * a; + double rm01 = -da * b; + double rm02 = -da * c; + double rm10 = -db * a; + double rm11 = 1.0 - db * b; + double rm12 = -db * c; + double rm20 = -dc * a; + double rm21 = -dc * b; + double rm22 = 1.0 - dc * c; + double rm30 = -dd * a; + double rm31 = -dd * b; + double rm32 = -dd * c; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + // matrix multiplication + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m33) + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + private Matrix4d reflectGeneric(double a, double b, double c, double d, Matrix4d dest) { + double da = a + a, db = b + b, dc = c + c, dd = d + d; + double rm00 = 1.0 - da * a; + double rm01 = -da * b; + double rm02 = -da * c; + double rm10 = -db * a; + double rm11 = 1.0 - db * b; + double rm12 = -db * c; + double rm20 = -dc * a; + double rm21 = -dc * b; + double rm22 = 1.0 - dc * c; + double rm30 = -dd * a; + double rm31 = -dd * b; + double rm32 = -dd * c; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + // matrix multiplication + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4d reflect(double a, double b, double c, double d) { + return reflect(a, b, c, d, this); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @return this + */ + public Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz) { + return reflect(nx, ny, nz, px, py, pz, this); + } + + public Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz, Matrix4d dest) { + double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz); + double nnx = nx * invLength; + double nny = ny * invLength; + double nnz = nz * invLength; + /* See: http://mathworld.wolfram.com/Plane.html */ + return reflect(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz, dest); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @return this + */ + public Matrix4d reflect(Vector3dc normal, Vector3dc point) { + return reflect(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z()); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation and a point on the plane. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation relative to an implied normal vector of (0, 0, 1) + * @param point + * a point on the plane + * @return this + */ + public Matrix4d reflect(Quaterniondc orientation, Vector3dc point) { + return reflect(orientation, point, this); + } + + public Matrix4d reflect(Quaterniondc orientation, Vector3dc point, Matrix4d dest) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + double normalX = orientation.x() * num3 + orientation.w() * num2; + double normalY = orientation.y() * num3 - orientation.w() * num1; + double normalZ = 1.0 - (orientation.x() * num1 + orientation.y() * num2); + return reflect(normalX, normalY, normalZ, point.x(), point.y(), point.z(), dest); + } + + public Matrix4d reflect(Vector3dc normal, Vector3dc point, Matrix4d dest) { + return reflect(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z(), dest); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4d reflection(double a, double b, double c, double d) { + double da = a + a, db = b + b, dc = c + c, dd = d + d; + _m00(1.0 - da * a). + _m01(-da * b). + _m02(-da * c). + _m03(0.0). + _m10(-db * a). + _m11(1.0 - db * b). + _m12(-db * c). + _m13(0.0). + _m20(-dc * a). + _m21(-dc * b). + _m22(1.0 - dc * c). + _m23(0.0). + _m30(-dd * a). + _m31(-dd * b). + _m32(-dd * c). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the plane normal and a point on the plane. + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @return this + */ + public Matrix4d reflection(double nx, double ny, double nz, double px, double py, double pz) { + double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz); + double nnx = nx * invLength; + double nny = ny * invLength; + double nnz = nz * invLength; + /* See: http://mathworld.wolfram.com/Plane.html */ + return reflection(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the plane normal and a point on the plane. + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @return this + */ + public Matrix4d reflection(Vector3dc normal, Vector3dc point) { + return reflection(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z()); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about a plane + * specified via the plane orientation and a point on the plane. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + * + * @param orientation + * the plane orientation + * @param point + * a point on the plane + * @return this + */ + public Matrix4d reflection(Quaterniondc orientation, Vector3dc point) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + double normalX = orientation.x() * num3 + orientation.w() * num2; + double normalY = orientation.y() * num3 - orientation.w() * num1; + double normalZ = 1.0 - (orientation.x() * num1 + orientation.y() * num2); + return reflection(normalX, normalY, normalZ, point.x(), point.y(), point.z()); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(double, double, double, double, double, double, boolean) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrtho(left, right, bottom, top, zNear, zFar, zZeroToOne); + return orthoGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d orthoGeneric(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + // calculate right matrix elements + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar); + double rm30 = (left + right) / (left - right); + double rm31 = (top + bottom) / (bottom - top); + double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20 * rm22) + ._m21(m21 * rm22) + ._m22(m22 * rm22) + ._m23(m23 * rm22) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4d dest) { + return ortho(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(double, double, double, double, double, double, boolean) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + return ortho(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar) { + return ortho(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(double, double, double, double, double, double, boolean) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne); + return orthoLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d orthoLHGeneric(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + // calculate right matrix elements + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear); + double rm30 = (left + right) / (left - right); + double rm31 = (top + bottom) / (bottom - top); + double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20 * rm22) + ._m21(m21 * rm22) + ._m22(m22 * rm22) + ._m23(m23 * rm22) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4d dest) { + return orthoLH(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(double, double, double, double, double, double, boolean) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + return orthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar) { + return orthoLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho(double, double, double, double, double, double, boolean) ortho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + _identity(); + _m00(2.0 / (right - left)). + _m11(2.0 / (top - bottom)). + _m22((zZeroToOne ? 1.0 : 2.0) / (zNear - zFar)). + _m30((right + left) / (left - right)). + _m31((top + bottom) / (bottom - top)). + _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)). + properties = PROPERTY_AFFINE; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho(double, double, double, double, double, double) ortho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar) { + return setOrtho(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #orthoLH(double, double, double, double, double, double, boolean) orthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + _identity(); + _m00(2.0 / (right - left)). + _m11(2.0 / (top - bottom)). + _m22((zZeroToOne ? 1.0 : 2.0) / (zFar - zNear)). + _m30((right + left) / (left - right)). + _m31((top + bottom) / (bottom - top)). + _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)). + properties = PROPERTY_AFFINE; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #orthoLH(double, double, double, double, double, double) orthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar) { + return setOrthoLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, boolean, Matrix4d) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(double, double, double, double, boolean) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrthoSymmetric(width, height, zNear, zFar, zZeroToOne); + return orthoSymmetricGeneric(width, height, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d orthoSymmetricGeneric(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + // calculate right matrix elements + double rm00 = 2.0 / width; + double rm11 = 2.0 / height; + double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar); + double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m20 * rm32 + m30) + ._m31(m21 * rm32 + m31) + ._m32(m22 * rm32 + m32) + ._m33(m23 * rm32 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20 * rm22) + ._m21(m21 * rm22) + ._m22(m22 * rm22) + ._m23(m23 * rm22) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, Matrix4d dest) { + return orthoSymmetric(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, boolean) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(double, double, double, double, boolean) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + return orthoSymmetric(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar) { + return orthoSymmetric(width, height, zNear, zFar, false, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, boolean, Matrix4d) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(double, double, double, double, boolean) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrthoSymmetricLH(width, height, zNear, zFar, zZeroToOne); + return orthoSymmetricLHGeneric(width, height, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d orthoSymmetricLHGeneric(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + // calculate right matrix elements + double rm00 = 2.0 / width; + double rm11 = 2.0 / height; + double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear); + double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m20 * rm32 + m30) + ._m31(m21 * rm32 + m31) + ._m32(m22 * rm32 + m32) + ._m33(m23 * rm32 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20 * rm22) + ._m21(m21 * rm22) + ._m22(m22 * rm22) + ._m23(m23 * rm22) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, Matrix4d dest) { + return orthoSymmetricLH(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, boolean) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(double, double, double, double, boolean) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + return orthoSymmetricLH(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar) { + return orthoSymmetricLH(width, height, zNear, zFar, false, this); + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, boolean) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetric(double, double, double, double, boolean) orthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetric(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setOrthoSymmetric(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + _identity(); + _m00(2.0 / width). + _m11(2.0 / height). + _m22((zZeroToOne ? 1.0 : 2.0) / (zNear - zFar)). + _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)). + properties = PROPERTY_AFFINE; + return this; + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetric(double, double, double, double) orthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetric(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4d setOrthoSymmetric(double width, double height, double zNear, double zFar) { + return setOrthoSymmetric(width, height, zNear, zFar, false); + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system using the given NDC z range. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, boolean) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetricLH(double, double, double, double, boolean) orthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetricLH(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setOrthoSymmetricLH(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + _identity(); + _m00(2.0 / width). + _m11(2.0 / height). + _m22((zZeroToOne ? 1.0 : 2.0) / (zFar - zNear)). + _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)). + properties = PROPERTY_AFFINE; + return this; + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * This method is equivalent to calling {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetricLH(double, double, double, double) orthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetricLH(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4d setOrthoSymmetricLH(double width, double height, double zNear, double zFar) { + return setOrthoSymmetricLH(width, height, zNear, zFar, false); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2D(double, double, double, double) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double, Matrix4d) + * @see #setOrtho2D(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d ortho2D(double left, double right, double bottom, double top, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrtho2D(left, right, bottom, top); + return ortho2DGeneric(left, right, bottom, top, dest); + } + private Matrix4d ortho2DGeneric(double left, double right, double bottom, double top, Matrix4d dest) { + // calculate right matrix elements + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm30 = (right + left) / (left - right); + double rm31 = (top + bottom) / (bottom - top); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(-m20) + ._m21(-m21) + ._m22(-m22) + ._m23(-m23) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2D(double, double, double, double) setOrtho2D()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double) + * @see #setOrtho2D(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4d ortho2D(double left, double right, double bottom, double top) { + return ortho2D(left, right, bottom, top, this); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2DLH(double, double, double, double) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double, Matrix4d) + * @see #setOrtho2DLH(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d ortho2DLH(double left, double right, double bottom, double top, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrtho2DLH(left, right, bottom, top); + return ortho2DLHGeneric(left, right, bottom, top, dest); + } + private Matrix4d ortho2DLHGeneric(double left, double right, double bottom, double top, Matrix4d dest) { + // calculate right matrix elements + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm30 = (right + left) / (left - right); + double rm31 = (top + bottom) / (bottom - top); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2DLH(double, double, double, double) setOrtho2DLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double) + * @see #setOrtho2DLH(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4d ortho2DLH(double left, double right, double bottom, double top) { + return ortho2DLH(left, right, bottom, top, this); + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with + * zNear=-1 and zFar=+1. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho2D(double, double, double, double) ortho2D()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double) + * @see #ortho2D(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4d setOrtho2D(double left, double right, double bottom, double top) { + if ((properties & PROPERTY_IDENTITY) == 0) + _identity(); + _m00(2.0 / (right - left)). + _m11(2.0 / (top - bottom)). + _m22(-1.0). + _m30((right + left) / (left - right)). + _m31((top + bottom) / (bottom - top)). + properties = PROPERTY_AFFINE; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho2DLH(double, double, double, double) ortho2DLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double) + * @see #ortho2DLH(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4d setOrtho2DLH(double left, double right, double bottom, double top) { + if ((properties & PROPERTY_IDENTITY) == 0) + _identity(); + _m00(2.0 / (right - left)). + _m11(2.0 / (top - bottom)). + _m30((right + left) / (left - right)). + _m31((top + bottom) / (bottom - top)). + properties = PROPERTY_AFFINE; + return this; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3dc, Vector3dc, Vector3dc) lookAt} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3dc, Vector3dc) setLookAlong()}. + * + * @see #lookAlong(double, double, double, double, double, double) + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4d lookAlong(Vector3dc dir, Vector3dc up) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3dc, Vector3dc, Vector3dc) lookAt} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3dc, Vector3dc) setLookAlong()}. + * + * @see #lookAlong(double, double, double, double, double, double) + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d lookAlong(Vector3dc dir, Vector3dc up, Matrix4d dest) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()} + * + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * @see #setLookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAlong(dirX, dirY, dirZ, upX, upY, upZ); + return lookAlongGeneric(dirX, dirY, dirZ, upX, upY, upZ, dest); + } + + private Matrix4d lookAlongGeneric(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix4d dest) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + // calculate right matrix elements + double rm00 = leftX; + double rm01 = upnX; + double rm02 = dirX; + double rm10 = leftY; + double rm11 = upnY; + double rm12 = dirY; + double rm20 = leftZ; + double rm21 = upnZ; + double rm22 = dirZ; + // perform optimized matrix multiplication + // introduce temporaries for dependent results + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + // set the rest of the matrix elements + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()} + * + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * @see #setLookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d lookAlong(double dirX, double dirY, double dirZ, + double upX, double upY, double upZ) { + return lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * This is equivalent to calling + * {@link #setLookAt(Vector3dc, Vector3dc, Vector3dc) setLookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(Vector3dc, Vector3dc)}. + * + * @see #setLookAlong(Vector3dc, Vector3dc) + * @see #lookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4d setLookAlong(Vector3dc dir, Vector3dc up) { + return setLookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * This is equivalent to calling + * {@link #setLookAt(double, double, double, double, double, double, double, double, double) + * setLookAt()} with eye = (0, 0, 0) and center = dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(double, double, double, double, double, double) lookAlong()} + * + * @see #setLookAlong(double, double, double, double, double, double) + * @see #lookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d setLookAlong(double dirX, double dirY, double dirZ, + double upX, double upY, double upZ) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + _m00(leftX). + _m01(upnX). + _m02(dirX). + _m03(0.0). + _m10(leftY). + _m11(upnY). + _m12(dirY). + _m13(0.0). + _m20(leftZ). + _m21(upnZ). + _m22(dirZ). + _m23(0.0). + _m30(0.0). + _m31(0.0). + _m32(0.0). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, that aligns + * -z with center - eye. + *

+ * In order to not make use of vectors to specify eye, center and up but use primitives, + * like in the GLU function, use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()} + * instead. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAt(Vector3dc, Vector3dc, Vector3dc) lookAt()}. + * + * @see #setLookAt(double, double, double, double, double, double, double, double, double) + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4d setLookAt(Vector3dc eye, Vector3dc center, Vector3dc up) { + return setLookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt}. + * + * @see #setLookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d setLookAt(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + return this. + _m00(leftX). + _m01(upnX). + _m02(dirX). + _m03(0.0). + _m10(leftY). + _m11(upnY). + _m12(dirY). + _m13(0.0). + _m20(leftZ). + _m21(upnZ). + _m22(dirZ). + _m23(0.0). + _m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)). + _m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)). + _m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)). + _m33(1.0). + _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d lookAt(Vector3dc eye, Vector3dc center, Vector3dc up, Matrix4d dest) { + return lookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4d lookAt(Vector3dc eye, Vector3dc center, Vector3dc up) { + return lookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}. + * + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAt(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d lookAt(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + else if ((properties & PROPERTY_PERSPECTIVE) != 0) + return lookAtPerspective(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + return lookAtGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + } + private Matrix4d lookAtGeneric(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4d dest) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + // calculate right matrix elements + double rm00 = leftX; + double rm01 = upnX; + double rm02 = dirX; + double rm10 = leftY; + double rm11 = upnY; + double rm12 = dirY; + double rm20 = leftZ; + double rm21 = upnZ; + double rm22 = dirZ; + double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + // perform optimized matrix multiplication + // compute last column first, because others do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + // introduce temporaries for dependent results + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + // set the rest of the matrix elements + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}. + * + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAt(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d lookAt(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ) { + return lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * This method assumes this to be a perspective transformation, obtained via + * {@link #frustum(double, double, double, double, double, double) frustum()} or {@link #perspective(double, double, double, double) perspective()} or + * one of their overloads. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}. + * + * @see #setLookAt(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d lookAtPerspective(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4d dest) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + double nm10 = m00 * leftY; + double nm20 = m00 * leftZ; + double nm21 = m11 * upnZ; + double nm30 = m00 * rm30; + double nm31 = m11 * rm31; + double nm32 = m22 * rm32 + m32; + double nm33 = m23 * rm32; + return dest + ._m00(m00 * leftX) + ._m01(m11 * upnX) + ._m02(m22 * dirX) + ._m03(m23 * dirX) + ._m10(nm10) + ._m11(m11 * upnY) + ._m12(m22 * dirY) + ._m13(m23 * dirY) + ._m20(nm20) + ._m21(nm21) + ._m22(m22 * dirZ) + ._m23(m23 * dirZ) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, that aligns + * +z with center - eye. + *

+ * In order to not make use of vectors to specify eye, center and up but use primitives, + * like in the GLU function, use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()} + * instead. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAtLH(Vector3dc, Vector3dc, Vector3dc) lookAt()}. + * + * @see #setLookAtLH(double, double, double, double, double, double, double, double, double) + * @see #lookAtLH(Vector3dc, Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4d setLookAtLH(Vector3dc eye, Vector3dc center, Vector3dc up) { + return setLookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAtLH(double, double, double, double, double, double, double, double, double) lookAtLH}. + * + * @see #setLookAtLH(Vector3dc, Vector3dc, Vector3dc) + * @see #lookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d setLookAtLH(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + _m00(leftX). + _m01(upnX). + _m02(dirX). + _m03(0.0). + _m10(leftY). + _m11(upnY). + _m12(dirY). + _m13(0.0). + _m20(leftZ). + _m21(upnZ). + _m22(dirZ). + _m23(0.0). + _m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)). + _m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)). + _m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #lookAtLH(double, double, double, double, double, double, double, double, double) + * @see #setLookAtLH(Vector3dc, Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d lookAtLH(Vector3dc eye, Vector3dc center, Vector3dc up, Matrix4d dest) { + return lookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #lookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4d lookAtLH(Vector3dc eye, Vector3dc center, Vector3dc up) { + return lookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}. + * + * @see #lookAtLH(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + else if ((properties & PROPERTY_PERSPECTIVE) != 0) + return lookAtPerspectiveLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + return lookAtLHGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + } + private Matrix4d lookAtLHGeneric(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4d dest) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + // calculate right matrix elements + double rm00 = leftX; + double rm01 = upnX; + double rm02 = dirX; + double rm10 = leftY; + double rm11 = upnY; + double rm12 = dirY; + double rm20 = leftZ; + double rm21 = upnZ; + double rm22 = dirZ; + double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + // introduce temporaries for dependent results + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + // perform optimized matrix multiplication + // compute last column first, because others do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + // set the rest of the matrix elements + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}. + * + * @see #lookAtLH(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ) { + return lookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * This method assumes this to be a perspective transformation, obtained via + * {@link #frustumLH(double, double, double, double, double, double) frustumLH()} or {@link #perspectiveLH(double, double, double, double) perspectiveLH()} or + * one of their overloads. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}. + * + * @see #setLookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d lookAtPerspectiveLH(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4d dest) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + double rm00 = leftX; + double rm01 = upnX; + double rm02 = dirX; + double rm10 = leftY; + double rm11 = upnY; + double rm12 = dirY; + double rm20 = leftZ; + double rm21 = upnZ; + double rm22 = dirZ; + double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + + double nm00 = m00 * rm00; + double nm01 = m11 * rm01; + double nm02 = m22 * rm02; + double nm03 = m23 * rm02; + double nm10 = m00 * rm10; + double nm11 = m11 * rm11; + double nm12 = m22 * rm12; + double nm13 = m23 * rm12; + double nm20 = m00 * rm20; + double nm21 = m11 * rm21; + double nm22 = m22 * rm22; + double nm23 = m23 * rm22; + double nm30 = m00 * rm30; + double nm31 = m11 * rm31; + double nm32 = m22 * rm32 + m32; + double nm33 = m23 * rm32; + dest._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + + return dest; + } + + /** + * This method is equivalent to calling: translate(w-1-2*x, h-1-2*y, 0).scale(w, h, 1) + *

+ * If M is this matrix and T the created transformation matrix, + * then the new matrix will be M * T. So when transforming a + * vector v with the new matrix by using M * T * v, the + * created transformation will be applied first! + * + * @param x + * the tile's x coordinate/index (should be in [0..w)) + * @param y + * the tile's y coordinate/index (should be in [0..h)) + * @param w + * the number of tiles along the x axis + * @param h + * the number of tiles along the y axis + * @return this + */ + public Matrix4d tile(int x, int y, int w, int h) { + return tile(x, y, w, h, this); + } + public Matrix4d tile(int x, int y, int w, int h, Matrix4d dest) { + float tx = w - 1 - (x<<1), ty = h - 1 - (y<<1); + return dest + ._m30(Math.fma(m00, tx, Math.fma(m10, ty, m30))) + ._m31(Math.fma(m01, tx, Math.fma(m11, ty, m31))) + ._m32(Math.fma(m02, tx, Math.fma(m12, ty, m32))) + ._m33(Math.fma(m03, tx, Math.fma(m13, ty, m33))) + ._m00(m00 * w) + ._m01(m01 * w) + ._m02(m02 * w) + ._m03(m03 * w) + ._m10(m10 * h) + ._m11(m11 * h) + ._m12(m12 * h) + ._m13(m13 * h) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspective(double, double, double, double, boolean) setPerspective}. + * + * @see #setPerspective(double, double, double, double, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setPerspective(fovy, aspect, zNear, zFar, zZeroToOne); + return perspectiveGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d perspectiveGeneric(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + double h = Math.tan(fovy * 0.5); + // calculate right matrix elements + double rm00 = 1.0 / (h * aspect); + double rm11 = 1.0 / h; + double rm22; + double rm32; + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + rm22 = e - 1.0; + rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear; + } else if (nearInf) { + double e = 1E-6; + rm22 = (zZeroToOne ? 0.0 : 1.0) - e; + rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + double nm20 = m20 * rm22 - m30; + double nm21 = m21 * rm22 - m31; + double nm22 = m22 * rm22 - m32; + double nm23 = m23 * rm22 - m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspective(double, double, double, double) setPerspective}. + * + * @see #setPerspective(double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, Matrix4d dest) { + return perspective(fovy, aspect, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspective(double, double, double, double, boolean) setPerspective}. + * + * @see #setPerspective(double, double, double, double, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne) { + return perspective(fovy, aspect, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspective(double, double, double, double) setPerspective}. + * + * @see #setPerspective(double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar) { + return perspective(fovy, aspect, zNear, zFar, this); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveRect(double, double, double, double, boolean) setPerspectiveRect}. + * + * @see #setPerspectiveRect(double, double, double, double, boolean) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setPerspectiveRect(width, height, zNear, zFar, zZeroToOne); + return perspectiveRectGeneric(width, height, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d perspectiveRectGeneric(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + double rm00 = (zNear + zNear) / width; + double rm11 = (zNear + zNear) / height; + double rm22, rm32; + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6f; + rm22 = e - 1.0; + rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear; + } else if (nearInf) { + double e = 1E-6f; + rm22 = (zZeroToOne ? 0.0 : 1.0) - e; + rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + double nm20 = m20 * rm22 - m30; + double nm21 = m21 * rm22 - m31; + double nm22 = m22 * rm22 - m32; + double nm23 = m23 * rm22 - m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveRect(double, double, double, double) setPerspectiveRect}. + * + * @see #setPerspectiveRect(double, double, double, double) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, Matrix4d dest) { + return perspectiveRect(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveRect(double, double, double, double, boolean) setPerspectiveRect}. + * + * @see #setPerspectiveRect(double, double, double, double, boolean) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + return perspectiveRect(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveRect(double, double, double, double) setPerspectiveRect}. + * + * @see #setPerspectiveRect(double, double, double, double) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar) { + return perspectiveRect(width, height, zNear, zFar, this); + } + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double, boolean) setPerspectiveOffCenter}. + * + * @see #setPerspectiveOffCenter(double, double, double, double, double, double, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne); + return perspectiveOffCenterGeneric(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d perspectiveOffCenterGeneric(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + double h = Math.tan(fovy * 0.5); + // calculate right matrix elements + double xScale = 1.0 / (h * aspect); + double yScale = 1.0 / h; + double rm00 = xScale; + double rm11 = yScale; + double offX = Math.tan(offAngleX), offY = Math.tan(offAngleY); + double rm20 = offX * xScale; + double rm21 = offY * yScale; + double rm22; + double rm32; + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + rm22 = e - 1.0; + rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear; + } else if (nearInf) { + double e = 1E-6; + rm22 = (zZeroToOne ? 0.0 : 1.0) - e; + rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30; + double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31; + double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32; + double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | PROPERTY_ORTHONORMAL | (rm20 == 0.0 && rm21 == 0.0 ? 0 : PROPERTY_PERSPECTIVE))); + return dest; + } + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double) setPerspectiveOffCenter}. + * + * @see #setPerspectiveOffCenter(double, double, double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, Matrix4d dest) { + return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false, dest); + } + + /** + * Apply an asymmetric off-center perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double, boolean) setPerspectiveOffCenter}. + * + * @see #setPerspectiveOffCenter(double, double, double, double, double, double, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, boolean zZeroToOne) { + return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double) setPerspectiveOffCenter}. + * + * @see #setPerspectiveOffCenter(double, double, double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar) { + return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, this); + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspective(double, double, double, double, boolean) perspective()}. + * + * @see #perspective(double, double, double, double, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setPerspective(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne) { + double h = Math.tan(fovy * 0.5); + _m00(1.0 / (h * aspect)). + _m01(0.0). + _m02(0.0). + _m03(0.0). + _m10(0.0). + _m11(1.0 / h). + _m12(0.0). + _m13(0.0). + _m20(0.0). + _m21(0.0); + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + _m22(e - 1.0). + _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear); + } else if (nearInf) { + double e = 1E-6; + _m22((zZeroToOne ? 0.0 : 1.0) - e). + _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar); + } else { + _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)). + _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + _m23(-1.0). + _m30(0.0). + _m31(0.0). + _m33(0.0). + properties = PROPERTY_PERSPECTIVE; + return this; + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspective(double, double, double, double) perspective()}. + * + * @see #perspective(double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d setPerspective(double fovy, double aspect, double zNear, double zFar) { + return setPerspective(fovy, aspect, zNear, zFar, false); + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveRect(double, double, double, double, boolean) perspectiveRect()}. + * + * @see #perspectiveRect(double, double, double, double, boolean) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setPerspectiveRect(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + this.zero(); + this._m00((zNear + zNear) / width); + this._m11((zNear + zNear) / height); + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + this._m22(e - 1.0); + this._m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear); + } else if (nearInf) { + double e = 1E-6f; + this._m22((zZeroToOne ? 0.0 : 1.0) - e); + this._m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar); + } else { + this._m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)); + this._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + this._m23(-1.0); + properties = PROPERTY_PERSPECTIVE; + return this; + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveRect(double, double, double, double) perspectiveRect()}. + * + * @see #perspectiveRect(double, double, double, double) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d setPerspectiveRect(double width, double height, double zNear, double zFar) { + return setPerspectiveRect(width, height, zNear, zFar, false); + } + + /** + * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed + * coordinate system using OpenGL's NDC z range of [-1..+1]. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveOffCenter(double, double, double, double, double, double) perspectiveOffCenter()}. + * + * @see #perspectiveOffCenter(double, double, double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d setPerspectiveOffCenter(double fovy, double offAngleX, double offAngleY, + double aspect, double zNear, double zFar) { + return setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false); + } + /** + * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed + * coordinate system using the given NDC z range. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveOffCenter(double, double, double, double, double, double) perspectiveOffCenter()}. + * + * @see #perspectiveOffCenter(double, double, double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setPerspectiveOffCenter(double fovy, double offAngleX, double offAngleY, + double aspect, double zNear, double zFar, boolean zZeroToOne) { + this.zero(); + double h = Math.tan(fovy * 0.5); + double xScale = 1.0 / (h * aspect), yScale = 1.0 / h; + _m00(xScale). + _m11(yScale); + double offX = Math.tan(offAngleX), offY = Math.tan(offAngleY); + _m20(offX * xScale). + _m21(offY * yScale); + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + _m22(e - 1.0). + _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear); + } else if (nearInf) { + double e = 1E-6; + _m22((zZeroToOne ? 0.0 : 1.0) - e). + _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar); + } else { + _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)). + _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + _m23(-1.0). + _m30(0.0). + _m31(0.0). + _m33(0.0). + properties = offAngleX == 0.0 && offAngleY == 0.0 ? PROPERTY_PERSPECTIVE : 0; + return this; + } + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveLH(double, double, double, double, boolean) setPerspectiveLH}. + * + * @see #setPerspectiveLH(double, double, double, double, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setPerspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne); + return perspectiveLHGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d perspectiveLHGeneric(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + double h = Math.tan(fovy * 0.5); + // calculate right matrix elements + double rm00 = 1.0 / (h * aspect); + double rm11 = 1.0 / h; + double rm22; + double rm32; + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + rm22 = 1.0 - e; + rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear; + } else if (nearInf) { + double e = 1E-6; + rm22 = (zZeroToOne ? 0.0 : 1.0) - e; + rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + double nm20 = m20 * rm22 + m30; + double nm21 = m21 * rm22 + m31; + double nm22 = m22 * rm22 + m32; + double nm23 = m23 * rm22 + m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveLH(double, double, double, double, boolean) setPerspectiveLH}. + * + * @see #setPerspectiveLH(double, double, double, double, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne) { + return perspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveLH(double, double, double, double) setPerspectiveLH}. + * + * @see #setPerspectiveLH(double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, Matrix4d dest) { + return perspectiveLH(fovy, aspect, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveLH(double, double, double, double) setPerspectiveLH}. + * + * @see #setPerspectiveLH(double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar) { + return perspectiveLH(fovy, aspect, zNear, zFar, this); + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range of [-1..+1]. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveLH(double, double, double, double, boolean) perspectiveLH()}. + * + * @see #perspectiveLH(double, double, double, double, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setPerspectiveLH(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne) { + double h = Math.tan(fovy * 0.5); + _m00(1.0 / (h * aspect)). + _m01(0.0). + _m02(0.0). + _m03(0.0). + _m10(0.0). + _m11(1.0 / h). + _m12(0.0). + _m13(0.0). + _m20(0.0). + _m21(0.0); + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + _m22(1.0 - e). + _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear); + } else if (nearInf) { + double e = 1E-6; + _m22((zZeroToOne ? 0.0 : 1.0) - e). + _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar); + } else { + _m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)). + _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + _m23(1.0). + _m30(0.0). + _m31(0.0). + _m33(0.0). + properties = PROPERTY_PERSPECTIVE; + return this; + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveLH(double, double, double, double) perspectiveLH()}. + * + * @see #perspectiveLH(double, double, double, double) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d setPerspectiveLH(double fovy, double aspect, double zNear, double zFar) { + return setPerspectiveLH(fovy, aspect, zNear, zFar, false); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustum(double, double, double, double, double, double, boolean) setFrustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustum(double, double, double, double, double, double, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setFrustum(left, right, bottom, top, zNear, zFar, zZeroToOne); + return frustumGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d frustumGeneric(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + // calculate right matrix elements + double rm00 = (zNear + zNear) / (right - left); + double rm11 = (zNear + zNear) / (top - bottom); + double rm20 = (right + left) / (right - left); + double rm21 = (top + bottom) / (top - bottom); + double rm22; + double rm32; + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + rm22 = e - 1.0; + rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear; + } else if (nearInf) { + double e = 1E-6; + rm22 = (zZeroToOne ? 0.0 : 1.0) - e; + rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30; + double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31; + double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32; + double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(0); + return dest; + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustum(double, double, double, double, double, double) setFrustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustum(double, double, double, double, double, double) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4d dest) { + return frustum(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustum(double, double, double, double, double, double, boolean) setFrustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustum(double, double, double, double, double, double, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + return frustum(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustum(double, double, double, double, double, double) setFrustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustum(double, double, double, double, double, double) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar) { + return frustum(left, right, bottom, top, zNear, zFar, this); + } + + /** + * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the perspective frustum transformation to an existing transformation, + * use {@link #frustum(double, double, double, double, double, double, boolean) frustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #frustum(double, double, double, double, double, double, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setFrustum(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + _identity(); + _m00((zNear + zNear) / (right - left)). + _m11((zNear + zNear) / (top - bottom)). + _m20((right + left) / (right - left)). + _m21((top + bottom) / (top - bottom)); + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + _m22(e - 1.0). + _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear); + } else if (nearInf) { + double e = 1E-6; + _m22((zZeroToOne ? 0.0 : 1.0) - e). + _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar); + } else { + _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)). + _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + _m23(-1.0). + _m33(0.0). + properties = this.m20 == 0.0 && this.m21 == 0.0 ? PROPERTY_PERSPECTIVE : 0; + return this; + } + + /** + * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective frustum transformation to an existing transformation, + * use {@link #frustum(double, double, double, double, double, double) frustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #frustum(double, double, double, double, double, double) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d setFrustum(double left, double right, double bottom, double top, double zNear, double zFar) { + return setFrustum(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustumLH(double, double, double, double, double, double, boolean) setFrustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustumLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setFrustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne); + return frustumLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest); + } + private Matrix4d frustumLHGeneric(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest) { + // calculate right matrix elements + double rm00 = (zNear + zNear) / (right - left); + double rm11 = (zNear + zNear) / (top - bottom); + double rm20 = (right + left) / (right - left); + double rm21 = (top + bottom) / (top - bottom); + double rm22; + double rm32; + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + rm22 = 1.0 - e; + rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear; + } else if (nearInf) { + double e = 1E-6; + rm22 = (zZeroToOne ? 0.0 : 1.0) - e; + rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30; + double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31; + double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32; + double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(0); + return dest; + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustumLH(double, double, double, double, double, double, boolean) setFrustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustumLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + return frustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustumLH(double, double, double, double, double, double) setFrustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustumLH(double, double, double, double, double, double) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4d dest) { + return frustumLH(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustumLH(double, double, double, double, double, double) setFrustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustumLH(double, double, double, double, double, double) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar) { + return frustumLH(left, right, bottom, top, zNear, zFar, this); + } + + /** + * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective frustum transformation to an existing transformation, + * use {@link #frustumLH(double, double, double, double, double, double, boolean) frustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #frustumLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4d setFrustumLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + _identity(); + _m00((zNear + zNear) / (right - left)). + _m11((zNear + zNear) / (top - bottom)). + _m20((right + left) / (right - left)). + _m21((top + bottom) / (top - bottom)); + boolean farInf = zFar > 0 && Double.isInfinite(zFar); + boolean nearInf = zNear > 0 && Double.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + double e = 1E-6; + _m22(1.0 - e). + _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear); + } else if (nearInf) { + double e = 1E-6; + _m22((zZeroToOne ? 0.0 : 1.0) - e). + _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar); + } else { + _m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)). + _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + _m23(1.0). + _m33(0.0). + properties = this.m20 == 0.0 && this.m21 == 0.0 ? PROPERTY_PERSPECTIVE : 0; + return this; + } + + /** + * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective frustum transformation to an existing transformation, + * use {@link #frustumLH(double, double, double, double, double, double) frustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #frustumLH(double, double, double, double, double, double) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4d setFrustumLH(double left, double right, double bottom, double top, double zNear, double zFar) { + return setFrustumLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to represent a perspective projection equivalent to the given intrinsic camera calibration parameters. + * The resulting matrix will be suited for a right-handed coordinate system using OpenGL's NDC z range of [-1..+1]. + *

+ * See: https://en.wikipedia.org/ + *

+ * Reference: http://ksimek.github.io/ + * + * @param alphaX + * specifies the focal length and scale along the X axis + * @param alphaY + * specifies the focal length and scale along the Y axis + * @param gamma + * the skew coefficient between the X and Y axis (may be 0) + * @param u0 + * the X coordinate of the principal point in image/sensor units + * @param v0 + * the Y coordinate of the principal point in image/sensor units + * @param imgWidth + * the width of the sensor/image image/sensor units + * @param imgHeight + * the height of the sensor/image image/sensor units + * @param near + * the distance to the near plane + * @param far + * the distance to the far plane + * @return this + */ + public Matrix4d setFromIntrinsic(double alphaX, double alphaY, double gamma, double u0, double v0, int imgWidth, int imgHeight, double near, double far) { + double l00 = 2.0 / imgWidth; + double l11 = 2.0 / imgHeight; + double l22 = 2.0 / (near - far); + this.m00 = l00 * alphaX; + this.m01 = 0.0; + this.m02 = 0.0; + this.m03 = 0.0; + this.m10 = l00 * gamma; + this.m11 = l11 * alphaY; + this.m12 = 0.0; + this.m13 = 0.0; + this.m20 = l00 * u0 - 1.0; + this.m21 = l11 * v0 - 1.0; + this.m22 = l22 * -(near + far) + (far + near) / (near - far); + this.m23 = -1.0; + this.m30 = 0.0; + this.m31 = 0.0; + this.m32 = l22 * -near * far; + this.m33 = 0.0; + this.properties = PROPERTY_PERSPECTIVE; + return this; + } + + public Vector4d frustumPlane(int plane, Vector4d dest) { + switch (plane) { + case PLANE_NX: + dest.set(m03 + m00, m13 + m10, m23 + m20, m33 + m30).normalize3(); + break; + case PLANE_PX: + dest.set(m03 - m00, m13 - m10, m23 - m20, m33 - m30).normalize3(); + break; + case PLANE_NY: + dest.set(m03 + m01, m13 + m11, m23 + m21, m33 + m31).normalize3(); + break; + case PLANE_PY: + dest.set(m03 - m01, m13 - m11, m23 - m21, m33 - m31).normalize3(); + break; + case PLANE_NZ: + dest.set(m03 + m02, m13 + m12, m23 + m22, m33 + m32).normalize3(); + break; + case PLANE_PZ: + dest.set(m03 - m02, m13 - m12, m23 - m22, m33 - m32).normalize3(); + break; + default: + throw new IllegalArgumentException("dest"); //$NON-NLS-1$ + } + return dest; + } + + public Vector3d frustumCorner(int corner, Vector3d dest) { + double d1, d2, d3; + double n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z; + switch (corner) { + case CORNER_NXNYNZ: // left, bottom, near + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom + n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near + break; + case CORNER_PXNYNZ: // right, bottom, near + n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right + n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom + n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near + break; + case CORNER_PXPYNZ: // right, top, near + n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right + n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top + n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near + break; + case CORNER_NXPYNZ: // left, top, near + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top + n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near + break; + case CORNER_PXNYPZ: // right, bottom, far + n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right + n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom + n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far + break; + case CORNER_NXNYPZ: // left, bottom, far + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom + n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far + break; + case CORNER_NXPYPZ: // left, top, far + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top + n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far + break; + case CORNER_PXPYPZ: // right, top, far + n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right + n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top + n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far + break; + default: + throw new IllegalArgumentException("corner"); //$NON-NLS-1$ + } + double c23x, c23y, c23z; + c23x = n2y * n3z - n2z * n3y; + c23y = n2z * n3x - n2x * n3z; + c23z = n2x * n3y - n2y * n3x; + double c31x, c31y, c31z; + c31x = n3y * n1z - n3z * n1y; + c31y = n3z * n1x - n3x * n1z; + c31z = n3x * n1y - n3y * n1x; + double c12x, c12y, c12z; + c12x = n1y * n2z - n1z * n2y; + c12y = n1z * n2x - n1x * n2z; + c12z = n1x * n2y - n1y * n2x; + double invDot = 1.0 / (n1x * c23x + n1y * c23y + n1z * c23z); + dest.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot; + dest.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot; + dest.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot; + return dest; + } + + public Vector3d perspectiveOrigin(Vector3d dest) { + /* + * Simply compute the intersection point of the left, right and top frustum plane. + */ + double d1, d2, d3; + double n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z; + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 - m00; n2y = m13 - m10; n2z = m23 - m20; d2 = m33 - m30; // right + n3x = m03 - m01; n3y = m13 - m11; n3z = m23 - m21; d3 = m33 - m31; // top + double c23x, c23y, c23z; + c23x = n2y * n3z - n2z * n3y; + c23y = n2z * n3x - n2x * n3z; + c23z = n2x * n3y - n2y * n3x; + double c31x, c31y, c31z; + c31x = n3y * n1z - n3z * n1y; + c31y = n3z * n1x - n3x * n1z; + c31z = n3x * n1y - n3y * n1x; + double c12x, c12y, c12z; + c12x = n1y * n2z - n1z * n2y; + c12y = n1z * n2x - n1x * n2z; + c12z = n1x * n2y - n1y * n2x; + double invDot = 1.0 / (n1x * c23x + n1y * c23y + n1z * c23z); + dest.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot; + dest.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot; + dest.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot; + return dest; + } + + public Vector3d perspectiveInvOrigin(Vector3d dest) { + double invW = 1.0 / m23; + dest.x = m20 * invW; + dest.y = m21 * invW; + dest.z = m22 * invW; + return dest; + } + + public double perspectiveFov() { + /* + * Compute the angle between the bottom and top frustum plane normals. + */ + double n1x, n1y, n1z, n2x, n2y, n2z; + n1x = m03 + m01; n1y = m13 + m11; n1z = m23 + m21; // bottom + n2x = m01 - m03; n2y = m11 - m13; n2z = m21 - m23; // top + double n1len = Math.sqrt(n1x * n1x + n1y * n1y + n1z * n1z); + double n2len = Math.sqrt(n2x * n2x + n2y * n2y + n2z * n2z); + return Math.acos((n1x * n2x + n1y * n2y + n1z * n2z) / (n1len * n2len)); + } + + public double perspectiveNear() { + return m32 / (m23 + m22); + } + + public double perspectiveFar() { + return m32 / (m22 - m23); + } + + public Vector3d frustumRayDir(double x, double y, Vector3d dest) { + /* + * This method works by first obtaining the frustum plane normals, + * then building the cross product to obtain the corner rays, + * and finally bilinearly interpolating to obtain the desired direction. + * The code below uses a condense form of doing all this making use + * of some mathematical identities to simplify the overall expression. + */ + double a = m10 * m23, b = m13 * m21, c = m10 * m21, d = m11 * m23, e = m13 * m20, f = m11 * m20; + double g = m03 * m20, h = m01 * m23, i = m01 * m20, j = m03 * m21, k = m00 * m23, l = m00 * m21; + double m = m00 * m13, n = m03 * m11, o = m00 * m11, p = m01 * m13, q = m03 * m10, r = m01 * m10; + double m1x, m1y, m1z; + m1x = (d + e + f - a - b - c) * (1.0 - y) + (a - b - c + d - e + f) * y; + m1y = (j + k + l - g - h - i) * (1.0 - y) + (g - h - i + j - k + l) * y; + m1z = (p + q + r - m - n - o) * (1.0 - y) + (m - n - o + p - q + r) * y; + double m2x, m2y, m2z; + m2x = (b - c - d + e + f - a) * (1.0 - y) + (a + b - c - d - e + f) * y; + m2y = (h - i - j + k + l - g) * (1.0 - y) + (g + h - i - j - k + l) * y; + m2z = (n - o - p + q + r - m) * (1.0 - y) + (m + n - o - p - q + r) * y; + dest.x = m1x * (1.0 - x) + m2x * x; + dest.y = m1y * (1.0 - x) + m2y * x; + dest.z = m1z * (1.0 - x) + m2z * x; + return dest.normalize(dest); + } + + public Vector3d positiveZ(Vector3d dir) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalizedPositiveZ(dir); + return positiveZGeneric(dir); + } + private Vector3d positiveZGeneric(Vector3d dir) { + return dir.set(m10 * m21 - m11 * m20, m20 * m01 - m21 * m00, m00 * m11 - m01 * m10).normalize(); + } + + public Vector3d normalizedPositiveZ(Vector3d dir) { + return dir.set(m02, m12, m22); + } + + public Vector3d positiveX(Vector3d dir) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalizedPositiveX(dir); + return positiveXGeneric(dir); + } + private Vector3d positiveXGeneric(Vector3d dir) { + return dir.set(m11 * m22 - m12 * m21, m02 * m21 - m01 * m22, m01 * m12 - m02 * m11).normalize(); + } + + public Vector3d normalizedPositiveX(Vector3d dir) { + return dir.set(m00, m10, m20); + } + + public Vector3d positiveY(Vector3d dir) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalizedPositiveY(dir); + return positiveYGeneric(dir); + } + private Vector3d positiveYGeneric(Vector3d dir) { + return dir.set(m12 * m20 - m10 * m22, m00 * m22 - m02 * m20, m02 * m10 - m00 * m12).normalize(); + } + + public Vector3d normalizedPositiveY(Vector3d dir) { + return dir.set(m01, m11, m21); + } + + public Vector3d originAffine(Vector3d dest) { + double a = m00 * m11 - m01 * m10; + double b = m00 * m12 - m02 * m10; + double d = m01 * m12 - m02 * m11; + double g = m20 * m31 - m21 * m30; + double h = m20 * m32 - m22 * m30; + double j = m21 * m32 - m22 * m31; + dest.x = -m10 * j + m11 * h - m12 * g; + dest.y = m00 * j - m01 * h + m02 * g; + dest.z = -m30 * d + m31 * b - m32 * a; + return dest; + } + + public Vector3d origin(Vector3d dest) { + if ((properties & PROPERTY_AFFINE) != 0) + return originAffine(dest); + return originGeneric(dest); + } + private Vector3d originGeneric(Vector3d dest) { + double a = m00 * m11 - m01 * m10; + double b = m00 * m12 - m02 * m10; + double c = m00 * m13 - m03 * m10; + double d = m01 * m12 - m02 * m11; + double e = m01 * m13 - m03 * m11; + double f = m02 * m13 - m03 * m12; + double g = m20 * m31 - m21 * m30; + double h = m20 * m32 - m22 * m30; + double i = m20 * m33 - m23 * m30; + double j = m21 * m32 - m22 * m31; + double k = m21 * m33 - m23 * m31; + double l = m22 * m33 - m23 * m32; + double det = a * l - b * k + c * j + d * i - e * h + f * g; + double invDet = 1.0 / det; + double nm30 = (-m10 * j + m11 * h - m12 * g) * invDet; + double nm31 = ( m00 * j - m01 * h + m02 * g) * invDet; + double nm32 = (-m30 * d + m31 * b - m32 * a) * invDet; + double nm33 = det / ( m20 * d - m21 * b + m22 * a); + double x = nm30 * nm33; + double y = nm31 * nm33; + double z = nm32 * nm33; + return dest.set(x, y, z); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction light. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param light + * the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4d shadow(Vector4dc light, double a, double b, double c, double d) { + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, this); + } + + public Matrix4d shadow(Vector4dc light, double a, double b, double c, double d, Matrix4d dest) { + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW). + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param lightX + * the x-component of the light's vector + * @param lightY + * the y-component of the light's vector + * @param lightZ + * the z-component of the light's vector + * @param lightW + * the w-component of the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d) { + return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, this); + } + + public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d, Matrix4d dest) { + // normalize plane + double invPlaneLen = Math.invsqrt(a*a + b*b + c*c); + double an = a * invPlaneLen; + double bn = b * invPlaneLen; + double cn = c * invPlaneLen; + double dn = d * invPlaneLen; + + double dot = an * lightX + bn * lightY + cn * lightZ + dn * lightW; + + // compute right matrix elements + double rm00 = dot - an * lightX; + double rm01 = -an * lightY; + double rm02 = -an * lightZ; + double rm03 = -an * lightW; + double rm10 = -bn * lightX; + double rm11 = dot - bn * lightY; + double rm12 = -bn * lightZ; + double rm13 = -bn * lightW; + double rm20 = -cn * lightX; + double rm21 = -cn * lightY; + double rm22 = dot - cn * lightZ; + double rm23 = -cn * lightW; + double rm30 = -dn * lightX; + double rm31 = -dn * lightY; + double rm32 = -dn * lightZ; + double rm33 = dot - dn * lightW; + + // matrix multiplication + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02 + m30 * rm03; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02 + m31 * rm03; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02 + m32 * rm03; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02 + m33 * rm03; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12 + m30 * rm13; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12 + m31 * rm13; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12 + m32 * rm13; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12 + m33 * rm13; + double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30 * rm23; + double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31 * rm23; + double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32 * rm23; + double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33 * rm23; + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30 * rm33) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31 * rm33) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32 * rm33) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33 * rm33) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + public Matrix4d shadow(Vector4dc light, Matrix4dc planeTransform, Matrix4d dest) { + // compute plane equation by transforming (y = 0) + double a = planeTransform.m10(); + double b = planeTransform.m11(); + double c = planeTransform.m12(); + double d = -a * planeTransform.m30() - b * planeTransform.m31() - c * planeTransform.m32(); + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction light. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param light + * the light's vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @return this + */ + public Matrix4d shadow(Vector4d light, Matrix4d planeTransform) { + return shadow(light, planeTransform, this); + } + + public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4dc planeTransform, Matrix4d dest) { + // compute plane equation by transforming (y = 0) + double a = planeTransform.m10(); + double b = planeTransform.m11(); + double c = planeTransform.m12(); + double d = -a * planeTransform.m30() - b * planeTransform.m31() - c * planeTransform.m32(); + return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW). + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param lightX + * the x-component of the light vector + * @param lightY + * the y-component of the light vector + * @param lightZ + * the z-component of the light vector + * @param lightW + * the w-component of the light vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @return this + */ + public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4dc planeTransform) { + return shadow(lightX, lightY, lightZ, lightW, planeTransform, this); + } + + /** + * Set this matrix to a cylindrical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos while constraining a cylindrical rotation around the given up vector. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @param up + * the rotation axis (must be {@link Vector3d#normalize() normalized}) + * @return this + */ + public Matrix4d billboardCylindrical(Vector3dc objPos, Vector3dc targetPos, Vector3dc up) { + double dirX = targetPos.x() - objPos.x(); + double dirY = targetPos.y() - objPos.y(); + double dirZ = targetPos.z() - objPos.z(); + // left = up x dir + double leftX = up.y() * dirZ - up.z() * dirY; + double leftY = up.z() * dirX - up.x() * dirZ; + double leftZ = up.x() * dirY - up.y() * dirX; + // normalize left + double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLen; + leftY *= invLeftLen; + leftZ *= invLeftLen; + // recompute dir by constraining rotation around 'up' + // dir = left x up + dirX = leftY * up.z() - leftZ * up.y(); + dirY = leftZ * up.x() - leftX * up.z(); + dirZ = leftX * up.y() - leftY * up.x(); + // normalize dir + double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLen; + dirY *= invDirLen; + dirZ *= invDirLen; + // set matrix elements + _m00(leftX). + _m01(leftY). + _m02(leftZ). + _m03(0.0). + _m10(up.x()). + _m11(up.y()). + _m12(up.z()). + _m13(0.0). + _m20(dirX). + _m21(dirY). + _m22(dirZ). + _m23(0.0). + _m30(objPos.x()). + _m31(objPos.y()). + _m32(objPos.z()). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + *

+ * If preserving an up vector is not necessary when rotating the +Z axis, then a shortest arc rotation can be obtained + * using {@link #billboardSpherical(Vector3dc, Vector3dc)}. + * + * @see #billboardSpherical(Vector3dc, Vector3dc) + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @param up + * the up axis used to orient the object + * @return this + */ + public Matrix4d billboardSpherical(Vector3dc objPos, Vector3dc targetPos, Vector3dc up) { + double dirX = targetPos.x() - objPos.x(); + double dirY = targetPos.y() - objPos.y(); + double dirZ = targetPos.z() - objPos.z(); + // normalize dir + double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLen; + dirY *= invDirLen; + dirZ *= invDirLen; + // left = up x dir + double leftX = up.y() * dirZ - up.z() * dirY; + double leftY = up.z() * dirX - up.x() * dirZ; + double leftZ = up.x() * dirY - up.y() * dirX; + // normalize left + double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLen; + leftY *= invLeftLen; + leftZ *= invLeftLen; + // up = dir x left + double upX = dirY * leftZ - dirZ * leftY; + double upY = dirZ * leftX - dirX * leftZ; + double upZ = dirX * leftY - dirY * leftX; + // set matrix elements + _m00(leftX). + _m01(leftY). + _m02(leftZ). + _m03(0.0). + _m10(upX). + _m11(upY). + _m12(upZ). + _m13(0.0). + _m20(dirX). + _m21(dirY). + _m22(dirZ). + _m23(0.0). + _m30(objPos.x()). + _m31(objPos.y()). + _m32(objPos.z()). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos using a shortest arc rotation by not preserving any up vector of the object. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + *

+ * In order to specify an up vector which needs to be maintained when rotating the +Z axis of the object, + * use {@link #billboardSpherical(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #billboardSpherical(Vector3dc, Vector3dc, Vector3dc) + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @return this + */ + public Matrix4d billboardSpherical(Vector3dc objPos, Vector3dc targetPos) { + double toDirX = targetPos.x() - objPos.x(); + double toDirY = targetPos.y() - objPos.y(); + double toDirZ = targetPos.z() - objPos.z(); + double x = -toDirY; + double y = toDirX; + double w = Math.sqrt(toDirX * toDirX + toDirY * toDirY + toDirZ * toDirZ) + toDirZ; + double invNorm = Math.invsqrt(x * x + y * y + w * w); + x *= invNorm; + y *= invNorm; + w *= invNorm; + double q00 = (x + x) * x; + double q11 = (y + y) * y; + double q01 = (x + x) * y; + double q03 = (x + x) * w; + double q13 = (y + y) * w; + _m00(1.0 - q11). + _m01(q01). + _m02(-q13). + _m03(0.0). + _m10(q01). + _m11(1.0 - q00). + _m12(q03). + _m13(0.0). + _m20(q13). + _m21(-q03). + _m22(1.0 - q11 - q00). + _m23(0.0). + _m30(objPos.x()). + _m31(objPos.y()). + _m32(objPos.z()). + _m33(1.0). + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(m00); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m01); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m02); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m03); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m10); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m11); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m12); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m13); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m20); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m21); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m22); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m23); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m30); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m31); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m32); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m33); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof Matrix4d)) + return false; + Matrix4d other = (Matrix4d) obj; + if (Double.doubleToLongBits(m00) != Double.doubleToLongBits(other.m00)) + return false; + if (Double.doubleToLongBits(m01) != Double.doubleToLongBits(other.m01)) + return false; + if (Double.doubleToLongBits(m02) != Double.doubleToLongBits(other.m02)) + return false; + if (Double.doubleToLongBits(m03) != Double.doubleToLongBits(other.m03)) + return false; + if (Double.doubleToLongBits(m10) != Double.doubleToLongBits(other.m10)) + return false; + if (Double.doubleToLongBits(m11) != Double.doubleToLongBits(other.m11)) + return false; + if (Double.doubleToLongBits(m12) != Double.doubleToLongBits(other.m12)) + return false; + if (Double.doubleToLongBits(m13) != Double.doubleToLongBits(other.m13)) + return false; + if (Double.doubleToLongBits(m20) != Double.doubleToLongBits(other.m20)) + return false; + if (Double.doubleToLongBits(m21) != Double.doubleToLongBits(other.m21)) + return false; + if (Double.doubleToLongBits(m22) != Double.doubleToLongBits(other.m22)) + return false; + if (Double.doubleToLongBits(m23) != Double.doubleToLongBits(other.m23)) + return false; + if (Double.doubleToLongBits(m30) != Double.doubleToLongBits(other.m30)) + return false; + if (Double.doubleToLongBits(m31) != Double.doubleToLongBits(other.m31)) + return false; + if (Double.doubleToLongBits(m32) != Double.doubleToLongBits(other.m32)) + return false; + if (Double.doubleToLongBits(m33) != Double.doubleToLongBits(other.m33)) + return false; + return true; + } + + public boolean equals(Matrix4dc m, double delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix4d)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m02, m.m02(), delta)) + return false; + if (!Runtime.equals(m03, m.m03(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + if (!Runtime.equals(m12, m.m12(), delta)) + return false; + if (!Runtime.equals(m13, m.m13(), delta)) + return false; + if (!Runtime.equals(m20, m.m20(), delta)) + return false; + if (!Runtime.equals(m21, m.m21(), delta)) + return false; + if (!Runtime.equals(m22, m.m22(), delta)) + return false; + if (!Runtime.equals(m23, m.m23(), delta)) + return false; + if (!Runtime.equals(m30, m.m30(), delta)) + return false; + if (!Runtime.equals(m31, m.m31(), delta)) + return false; + if (!Runtime.equals(m32, m.m32(), delta)) + return false; + if (!Runtime.equals(m33, m.m33(), delta)) + return false; + return true; + } + + public Matrix4d pick(double x, double y, double width, double height, int[] viewport, Matrix4d dest) { + double sx = viewport[2] / width; + double sy = viewport[3] / height; + double tx = (viewport[2] + 2.0 * (viewport[0] - x)) / width; + double ty = (viewport[3] + 2.0 * (viewport[1] - y)) / height; + dest._m30(m00 * tx + m10 * ty + m30) + ._m31(m01 * tx + m11 * ty + m31) + ._m32(m02 * tx + m12 * ty + m32) + ._m33(m03 * tx + m13 * ty + m33) + ._m00(m00 * sx) + ._m01(m01 * sx) + ._m02(m02 * sx) + ._m03(m03 * sx) + ._m10(m10 * sy) + ._m11(m11 * sy) + ._m12(m12 * sy) + ._m13(m13 * sy) + ._properties(0); + return dest; + } + + /** + * Apply a picking transformation to this matrix using the given window coordinates (x, y) as the pick center + * and the given (width, height) as the size of the picking region in window coordinates. + * + * @param x + * the x coordinate of the picking region center in window coordinates + * @param y + * the y coordinate of the picking region center in window coordinates + * @param width + * the width of the picking region in window coordinates + * @param height + * the height of the picking region in window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @return this + */ + public Matrix4d pick(double x, double y, double width, double height, int[] viewport) { + return pick(x, y, width, height, viewport, this); + } + + public boolean isAffine() { + return m03 == 0.0 && m13 == 0.0 && m23 == 0.0 && m33 == 1.0; + } + + /** + * Exchange the values of this matrix with the given other matrix. + * + * @param other + * the other matrix to exchange the values with + * @return this + */ + public Matrix4d swap(Matrix4d other) { + double tmp; + tmp = m00; m00 = other.m00; other.m00 = tmp; + tmp = m01; m01 = other.m01; other.m01 = tmp; + tmp = m02; m02 = other.m02; other.m02 = tmp; + tmp = m03; m03 = other.m03; other.m03 = tmp; + tmp = m10; m10 = other.m10; other.m10 = tmp; + tmp = m11; m11 = other.m11; other.m11 = tmp; + tmp = m12; m12 = other.m12; other.m12 = tmp; + tmp = m13; m13 = other.m13; other.m13 = tmp; + tmp = m20; m20 = other.m20; other.m20 = tmp; + tmp = m21; m21 = other.m21; other.m21 = tmp; + tmp = m22; m22 = other.m22; other.m22 = tmp; + tmp = m23; m23 = other.m23; other.m23 = tmp; + tmp = m30; m30 = other.m30; other.m30 = tmp; + tmp = m31; m31 = other.m31; other.m31 = tmp; + tmp = m32; m32 = other.m32; other.m32 = tmp; + tmp = m33; m33 = other.m33; other.m33 = tmp; + int props = properties; + this.properties = other.properties; + other.properties = props; + return this; + } + + public Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY, Matrix4d dest) { + double m30 = m20 * -radius + this.m30; + double m31 = m21 * -radius + this.m31; + double m32 = m22 * -radius + this.m32; + double m33 = m23 * -radius + this.m33; + double sin = Math.sin(angleX); + double cos = Math.cosFromSin(sin, angleX); + double nm10 = m10 * cos + m20 * sin; + double nm11 = m11 * cos + m21 * sin; + double nm12 = m12 * cos + m22 * sin; + double nm13 = m13 * cos + m23 * sin; + double m20 = this.m20 * cos - m10 * sin; + double m21 = this.m21 * cos - m11 * sin; + double m22 = this.m22 * cos - m12 * sin; + double m23 = this.m23 * cos - m13 * sin; + sin = Math.sin(angleY); + cos = Math.cosFromSin(sin, angleY); + double nm00 = m00 * cos - m20 * sin; + double nm01 = m01 * cos - m21 * sin; + double nm02 = m02 * cos - m22 * sin; + double nm03 = m03 * cos - m23 * sin; + double nm20 = m00 * sin + m20 * cos; + double nm21 = m01 * sin + m21 * cos; + double nm22 = m02 * sin + m22 * cos; + double nm23 = m03 * sin + m23 * cos; + dest._m30(-nm00 * centerX - nm10 * centerY - nm20 * centerZ + m30) + ._m31(-nm01 * centerX - nm11 * centerY - nm21 * centerZ + m31) + ._m32(-nm02 * centerX - nm12 * centerY - nm22 * centerZ + m32) + ._m33(-nm03 * centerX - nm13 * centerY - nm23 * centerZ + m33) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + public Matrix4d arcball(double radius, Vector3dc center, double angleX, double angleY, Matrix4d dest) { + return arcball(radius, center.x(), center.y(), center.z(), angleX, angleY, dest); + } + + /** + * Apply an arcball view transformation to this matrix with the given radius and center (centerX, centerY, centerZ) + * position of the arcball and the specified X and Y rotation angles. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ) + * + * @param radius + * the arcball radius + * @param centerX + * the x coordinate of the center position of the arcball + * @param centerY + * the y coordinate of the center position of the arcball + * @param centerZ + * the z coordinate of the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @return this + */ + public Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY) { + return arcball(radius, centerX, centerY, centerZ, angleX, angleY, this); + } + + /** + * Apply an arcball view transformation to this matrix with the given radius and center + * position of the arcball and the specified X and Y rotation angles. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z) + * + * @param radius + * the arcball radius + * @param center + * the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @return this + */ + public Matrix4d arcball(double radius, Vector3dc center, double angleX, double angleY) { + return arcball(radius, center.x(), center.y(), center.z(), angleX, angleY, this); + } + + /** + * Compute the axis-aligned bounding box of the frustum described by this matrix and store the minimum corner + * coordinates in the given min and the maximum corner coordinates in the given max vector. + *

+ * The matrix this is assumed to be the {@link #invert() inverse} of the origial view-projection matrix + * for which to compute the axis-aligned bounding box in world-space. + *

+ * The axis-aligned bounding box of the unit frustum is (-1, -1, -1), (1, 1, 1). + * + * @param min + * will hold the minimum corner coordinates of the axis-aligned bounding box + * @param max + * will hold the maximum corner coordinates of the axis-aligned bounding box + * @return this + */ + public Matrix4d frustumAabb(Vector3d min, Vector3d max) { + double minX = Double.POSITIVE_INFINITY; + double minY = Double.POSITIVE_INFINITY; + double minZ = Double.POSITIVE_INFINITY; + double maxX = Double.NEGATIVE_INFINITY; + double maxY = Double.NEGATIVE_INFINITY; + double maxZ = Double.NEGATIVE_INFINITY; + for (int t = 0; t < 8; t++) { + double x = ((t & 1) << 1) - 1.0; + double y = (((t >>> 1) & 1) << 1) - 1.0; + double z = (((t >>> 2) & 1) << 1) - 1.0; + double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33); + double nx = (m00 * x + m10 * y + m20 * z + m30) * invW; + double ny = (m01 * x + m11 * y + m21 * z + m31) * invW; + double nz = (m02 * x + m12 * y + m22 * z + m32) * invW; + minX = minX < nx ? minX : nx; + minY = minY < ny ? minY : ny; + minZ = minZ < nz ? minZ : nz; + maxX = maxX > nx ? maxX : nx; + maxY = maxY > ny ? maxY : ny; + maxZ = maxZ > nz ? maxZ : nz; + } + min.x = minX; + min.y = minY; + min.z = minZ; + max.x = maxX; + max.y = maxY; + max.z = maxZ; + return this; + } + + public Matrix4d projectedGridRange(Matrix4dc projector, double sLower, double sUpper, Matrix4d dest) { + // Compute intersection with frustum edges and plane + double minX = Double.POSITIVE_INFINITY, minY = Double.POSITIVE_INFINITY; + double maxX = Double.NEGATIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY; + boolean intersection = false; + for (int t = 0; t < 3 * 4; t++) { + double c0X, c0Y, c0Z; + double c1X, c1Y, c1Z; + if (t < 4) { + // all x edges + c0X = -1; c1X = +1; + c0Y = c1Y = ((t & 1) << 1) - 1.0; + c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0; + } else if (t < 8) { + // all y edges + c0Y = -1; c1Y = +1; + c0X = c1X = ((t & 1) << 1) - 1.0; + c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0; + } else { + // all z edges + c0Z = -1; c1Z = +1; + c0X = c1X = ((t & 1) << 1) - 1.0; + c0Y = c1Y = (((t >>> 1) & 1) << 1) - 1.0; + } + // unproject corners + double invW = 1.0 / (m03 * c0X + m13 * c0Y + m23 * c0Z + m33); + double p0x = (m00 * c0X + m10 * c0Y + m20 * c0Z + m30) * invW; + double p0y = (m01 * c0X + m11 * c0Y + m21 * c0Z + m31) * invW; + double p0z = (m02 * c0X + m12 * c0Y + m22 * c0Z + m32) * invW; + invW = 1.0 / (m03 * c1X + m13 * c1Y + m23 * c1Z + m33); + double p1x = (m00 * c1X + m10 * c1Y + m20 * c1Z + m30) * invW; + double p1y = (m01 * c1X + m11 * c1Y + m21 * c1Z + m31) * invW; + double p1z = (m02 * c1X + m12 * c1Y + m22 * c1Z + m32) * invW; + double dirX = p1x - p0x; + double dirY = p1y - p0y; + double dirZ = p1z - p0z; + double invDenom = 1.0 / dirY; + // test for intersection + for (int s = 0; s < 2; s++) { + double isectT = -(p0y + (s == 0 ? sLower : sUpper)) * invDenom; + if (isectT >= 0.0 && isectT <= 1.0) { + intersection = true; + // project with projector matrix + double ix = p0x + isectT * dirX; + double iz = p0z + isectT * dirZ; + invW = 1.0 / (projector.m03() * ix + projector.m23() * iz + projector.m33()); + double px = (projector.m00() * ix + projector.m20() * iz + projector.m30()) * invW; + double py = (projector.m01() * ix + projector.m21() * iz + projector.m31()) * invW; + minX = minX < px ? minX : px; + minY = minY < py ? minY : py; + maxX = maxX > px ? maxX : px; + maxY = maxY > py ? maxY : py; + } + } + } + if (!intersection) + return null; // <- projected grid is not visible + dest.set(maxX - minX, 0, 0, 0, 0, maxY - minY, 0, 0, 0, 0, 1, 0, minX, minY, 0, 1) + ._properties(PROPERTY_AFFINE); + return dest; + } + + public Matrix4d perspectiveFrustumSlice(double near, double far, Matrix4d dest) { + double invOldNear = (m23 + m22) / m32; + double invNearFar = 1.0 / (near - far); + dest._m00(m00 * invOldNear * near) + ._m01(m01) + ._m02(m02) + ._m03(m03) + ._m10(m10) + ._m11(m11 * invOldNear * near) + ._m12(m12) + ._m13(m13) + ._m20(m20) + ._m21(m21) + ._m22((far + near) * invNearFar) + ._m23(m23) + ._m30(m30) + ._m31(m31) + ._m32((far + far) * near * invNearFar) + ._m33(m33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + public Matrix4d orthoCrop(Matrix4dc view, Matrix4d dest) { + // determine min/max world z and min/max orthographically view-projected x/y + double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY; + double minY = Double.POSITIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY; + double minZ = Double.POSITIVE_INFINITY, maxZ = Double.NEGATIVE_INFINITY; + for (int t = 0; t < 8; t++) { + double x = ((t & 1) << 1) - 1.0; + double y = (((t >>> 1) & 1) << 1) - 1.0; + double z = (((t >>> 2) & 1) << 1) - 1.0; + double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33); + double wx = (m00 * x + m10 * y + m20 * z + m30) * invW; + double wy = (m01 * x + m11 * y + m21 * z + m31) * invW; + double wz = (m02 * x + m12 * y + m22 * z + m32) * invW; + invW = 1.0 / (view.m03() * wx + view.m13() * wy + view.m23() * wz + view.m33()); + double vx = view.m00() * wx + view.m10() * wy + view.m20() * wz + view.m30(); + double vy = view.m01() * wx + view.m11() * wy + view.m21() * wz + view.m31(); + double vz = (view.m02() * wx + view.m12() * wy + view.m22() * wz + view.m32()) * invW; + minX = minX < vx ? minX : vx; + maxX = maxX > vx ? maxX : vx; + minY = minY < vy ? minY : vy; + maxY = maxY > vy ? maxY : vy; + minZ = minZ < vz ? minZ : vz; + maxZ = maxZ > vz ? maxZ : vz; + } + // build crop projection matrix to fit 'this' frustum into view + return dest.setOrtho(minX, maxX, minY, maxY, -maxZ, -minZ); + } + + /** + * Set this matrix to a perspective transformation that maps the trapezoid spanned by the four corner coordinates + * (p0x, p0y), (p1x, p1y), (p2x, p2y) and (p3x, p3y) to the unit square [(-1, -1)..(+1, +1)]. + *

+ * The corner coordinates are given in counter-clockwise order starting from the left corner on the smaller parallel side of the trapezoid + * seen when looking at the trapezoid oriented with its shorter parallel edge at the bottom and its longer parallel edge at the top. + *

+ * Reference: Trapezoidal Shadow Maps (TSM) - Recipe + * + * @param p0x + * the x coordinate of the left corner at the shorter edge of the trapezoid + * @param p0y + * the y coordinate of the left corner at the shorter edge of the trapezoid + * @param p1x + * the x coordinate of the right corner at the shorter edge of the trapezoid + * @param p1y + * the y coordinate of the right corner at the shorter edge of the trapezoid + * @param p2x + * the x coordinate of the right corner at the longer edge of the trapezoid + * @param p2y + * the y coordinate of the right corner at the longer edge of the trapezoid + * @param p3x + * the x coordinate of the left corner at the longer edge of the trapezoid + * @param p3y + * the y coordinate of the left corner at the longer edge of the trapezoid + * @return this + */ + public Matrix4d trapezoidCrop(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y) { + double aX = p1y - p0y, aY = p0x - p1x; + double nm00 = aY; + double nm10 = -aX; + double nm30 = aX * p0y - aY * p0x; + double nm01 = aX; + double nm11 = aY; + double nm31 = -(aX * p0x + aY * p0y); + double c3x = nm00 * p3x + nm10 * p3y + nm30; + double c3y = nm01 * p3x + nm11 * p3y + nm31; + double s = -c3x / c3y; + nm00 += s * nm01; + nm10 += s * nm11; + nm30 += s * nm31; + double d1x = nm00 * p1x + nm10 * p1y + nm30; + double d2x = nm00 * p2x + nm10 * p2y + nm30; + double d = d1x * c3y / (d2x - d1x); + nm31 += d; + double sx = 2.0 / d2x; + double sy = 1.0 / (c3y + d); + double u = (sy + sy) * d / (1.0 - sy * d); + double m03 = nm01 * sy; + double m13 = nm11 * sy; + double m33 = nm31 * sy; + nm01 = (u + 1.0) * m03; + nm11 = (u + 1.0) * m13; + nm31 = (u + 1.0) * m33 - u; + nm00 = sx * nm00 - m03; + nm10 = sx * nm10 - m13; + nm30 = sx * nm30 - m33; + set(nm00, nm01, 0, m03, + nm10, nm11, 0, m13, + 0, 0, 1, 0, + nm30, nm31, 0, m33); + properties = 0; + return this; + } + + public Matrix4d transformAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vector3d outMin, Vector3d outMax) { + double xax = m00 * minX, xay = m01 * minX, xaz = m02 * minX; + double xbx = m00 * maxX, xby = m01 * maxX, xbz = m02 * maxX; + double yax = m10 * minY, yay = m11 * minY, yaz = m12 * minY; + double ybx = m10 * maxY, yby = m11 * maxY, ybz = m12 * maxY; + double zax = m20 * minZ, zay = m21 * minZ, zaz = m22 * minZ; + double zbx = m20 * maxZ, zby = m21 * maxZ, zbz = m22 * maxZ; + double xminx, xminy, xminz, yminx, yminy, yminz, zminx, zminy, zminz; + double xmaxx, xmaxy, xmaxz, ymaxx, ymaxy, ymaxz, zmaxx, zmaxy, zmaxz; + if (xax < xbx) { + xminx = xax; + xmaxx = xbx; + } else { + xminx = xbx; + xmaxx = xax; + } + if (xay < xby) { + xminy = xay; + xmaxy = xby; + } else { + xminy = xby; + xmaxy = xay; + } + if (xaz < xbz) { + xminz = xaz; + xmaxz = xbz; + } else { + xminz = xbz; + xmaxz = xaz; + } + if (yax < ybx) { + yminx = yax; + ymaxx = ybx; + } else { + yminx = ybx; + ymaxx = yax; + } + if (yay < yby) { + yminy = yay; + ymaxy = yby; + } else { + yminy = yby; + ymaxy = yay; + } + if (yaz < ybz) { + yminz = yaz; + ymaxz = ybz; + } else { + yminz = ybz; + ymaxz = yaz; + } + if (zax < zbx) { + zminx = zax; + zmaxx = zbx; + } else { + zminx = zbx; + zmaxx = zax; + } + if (zay < zby) { + zminy = zay; + zmaxy = zby; + } else { + zminy = zby; + zmaxy = zay; + } + if (zaz < zbz) { + zminz = zaz; + zmaxz = zbz; + } else { + zminz = zbz; + zmaxz = zaz; + } + outMin.x = xminx + yminx + zminx + m30; + outMin.y = xminy + yminy + zminy + m31; + outMin.z = xminz + yminz + zminz + m32; + outMax.x = xmaxx + ymaxx + zmaxx + m30; + outMax.y = xmaxy + ymaxy + zmaxy + m31; + outMax.z = xmaxz + ymaxz + zmaxz + m32; + return this; + } + + public Matrix4d transformAab(Vector3dc min, Vector3dc max, Vector3d outMin, Vector3d outMax) { + return transformAab(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), outMin, outMax); + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Matrix4d lerp(Matrix4dc other, double t) { + return lerp(other, t, this); + } + + public Matrix4d lerp(Matrix4dc other, double t, Matrix4d dest) { + dest._m00(Math.fma(other.m00() - m00, t, m00)) + ._m01(Math.fma(other.m01() - m01, t, m01)) + ._m02(Math.fma(other.m02() - m02, t, m02)) + ._m03(Math.fma(other.m03() - m03, t, m03)) + ._m10(Math.fma(other.m10() - m10, t, m10)) + ._m11(Math.fma(other.m11() - m11, t, m11)) + ._m12(Math.fma(other.m12() - m12, t, m12)) + ._m13(Math.fma(other.m13() - m13, t, m13)) + ._m20(Math.fma(other.m20() - m20, t, m20)) + ._m21(Math.fma(other.m21() - m21, t, m21)) + ._m22(Math.fma(other.m22() - m22, t, m22)) + ._m23(Math.fma(other.m23() - m23, t, m23)) + ._m30(Math.fma(other.m30() - m30, t, m30)) + ._m31(Math.fma(other.m31() - m31, t, m31)) + ._m32(Math.fma(other.m32() - m32, t, m32)) + ._m33(Math.fma(other.m33() - m33, t, m33)) + ._properties(properties & other.properties()); + return dest; + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3dc, Vector3dc) rotationTowards()}. + *

+ * This method is equivalent to calling: mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine(), dest) + * + * @see #rotateTowards(double, double, double, double, double, double, Matrix4d) + * @see #rotationTowards(Vector3dc, Vector3dc) + * + * @param direction + * the direction to rotate towards + * @param up + * the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateTowards(Vector3dc direction, Vector3dc up, Matrix4d dest) { + return rotateTowards(direction.x(), direction.y(), direction.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3dc, Vector3dc) rotationTowards()}. + *

+ * This method is equivalent to calling: mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine()) + * + * @see #rotateTowards(double, double, double, double, double, double) + * @see #rotationTowards(Vector3dc, Vector3dc) + * + * @param direction + * the direction to orient towards + * @param up + * the up vector + * @return this + */ + public Matrix4d rotateTowards(Vector3dc direction, Vector3dc up) { + return rotateTowards(direction.x(), direction.y(), direction.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ). + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}. + *

+ * This method is equivalent to calling: mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine()) + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + return rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}. + *

+ * This method is equivalent to calling: mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine(), dest) + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix4d dest) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double ndirX = dirX * invDirLength; + double ndirY = dirY * invDirLength; + double ndirZ = dirZ * invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = ndirY * leftZ - ndirZ * leftY; + double upnY = ndirZ * leftX - ndirX * leftZ; + double upnZ = ndirX * leftY - ndirY * leftX; + double rm00 = leftX; + double rm01 = leftY; + double rm02 = leftZ; + double rm10 = upnX; + double rm11 = upnY; + double rm12 = upnZ; + double rm20 = ndirX; + double rm21 = ndirY; + double rm22 = ndirZ; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with dir. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine() + * + * @see #rotationTowards(Vector3dc, Vector3dc) + * @see #rotateTowards(double, double, double, double, double, double) + * + * @param dir + * the direction to orient the local -z axis towards + * @param up + * the up vector + * @return this + */ + public Matrix4d rotationTowards(Vector3dc dir, Vector3dc up) { + return rotationTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with dir. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine() + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d rotationTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double ndirX = dirX * invDirLength; + double ndirY = dirY * invDirLength; + double ndirZ = dirZ * invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = ndirY * leftZ - ndirZ * leftY; + double upnY = ndirZ * leftX - ndirX * leftZ; + double upnZ = ndirX * leftY - ndirY * leftX; + if ((properties & PROPERTY_IDENTITY) == 0) + this._identity(); + this.m00 = leftX; + this.m01 = leftY; + this.m02 = leftZ; + this.m10 = upnX; + this.m11 = upnY; + this.m12 = upnZ; + this.m20 = ndirX; + this.m21 = ndirY; + this.m22 = ndirZ; + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that translates to the given pos and aligns the local -z + * axis with dir. + *

+ * This method is equivalent to calling: translation(pos).rotateTowards(dir, up) + * + * @see #translation(Vector3dc) + * @see #rotateTowards(Vector3dc, Vector3dc) + * + * @param pos + * the position to translate to + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @return this + */ + public Matrix4d translationRotateTowards(Vector3dc pos, Vector3dc dir, Vector3dc up) { + return translationRotateTowards(pos.x(), pos.y(), pos.z(), dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that translates to the given (posX, posY, posZ) and aligns the local -z + * axis with (dirX, dirY, dirZ). + *

+ * This method is equivalent to calling: translation(posX, posY, posZ).rotateTowards(dirX, dirY, dirZ, upX, upY, upZ) + * + * @see #translation(double, double, double) + * @see #rotateTowards(double, double, double, double, double, double) + * + * @param posX + * the x-coordinate of the position to translate to + * @param posY + * the y-coordinate of the position to translate to + * @param posZ + * the z-coordinate of the position to translate to + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4d translationRotateTowards(double posX, double posY, double posZ, double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double ndirX = dirX * invDirLength; + double ndirY = dirY * invDirLength; + double ndirZ = dirZ * invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = ndirY * leftZ - ndirZ * leftY; + double upnY = ndirZ * leftX - ndirX * leftZ; + double upnZ = ndirX * leftY - ndirY * leftX; + this.m00 = leftX; + this.m01 = leftY; + this.m02 = leftZ; + this.m03 = 0.0; + this.m10 = upnX; + this.m11 = upnY; + this.m12 = upnZ; + this.m13 = 0.0; + this.m20 = ndirX; + this.m21 = ndirY; + this.m22 = ndirZ; + this.m23 = 0.0; + this.m30 = posX; + this.m31 = posY; + this.m32 = posZ; + this.m33 = 1.0; + properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL; + return this; + } + + public Vector3d getEulerAnglesZYX(Vector3d dest) { + dest.x = Math.atan2(m12, m22); + dest.y = Math.atan2(-m02, Math.sqrt(1.0 - m02 * m02)); + dest.z = Math.atan2(m01, m00); + return dest; + } + + public Vector3d getEulerAnglesXYZ(Vector3d dest) { + dest.x = Math.atan2(-m21, m22); + dest.y = Math.atan2(m20, Math.sqrt(1.0 - m20 * m20)); + dest.z = Math.atan2(-m10, m00); + return dest; + } + + /** + * Compute the extents of the coordinate system before this {@link #isAffine() affine} transformation was applied + * and store the resulting corner coordinates in corner and the span vectors in + * xDir, yDir and zDir. + *

+ * That means, given the maximum extents of the coordinate system between [-1..+1] in all dimensions, + * this method returns one corner and the length and direction of the three base axis vectors in the coordinate + * system before this transformation is applied, which transforms into the corner coordinates [-1, +1]. + *

+ * This method is equivalent to computing at least three adjacent corners using {@link #frustumCorner(int, Vector3d)} + * and subtracting them to obtain the length and direction of the span vectors. + * + * @param corner + * will hold one corner of the span (usually the corner {@link Matrix4dc#CORNER_NXNYNZ}) + * @param xDir + * will hold the direction and length of the span along the positive X axis + * @param yDir + * will hold the direction and length of the span along the positive Y axis + * @param zDir + * will hold the direction and length of the span along the positive z axis + * @return this + */ + public Matrix4d affineSpan(Vector3d corner, Vector3d xDir, Vector3d yDir, Vector3d zDir) { + double a = m10 * m22, b = m10 * m21, c = m10 * m02, d = m10 * m01; + double e = m11 * m22, f = m11 * m20, g = m11 * m02, h = m11 * m00; + double i = m12 * m21, j = m12 * m20, k = m12 * m01, l = m12 * m00; + double m = m20 * m02, n = m20 * m01, o = m21 * m02, p = m21 * m00; + double q = m22 * m01, r = m22 * m00; + double s = 1.0 / (m00 * m11 - m01 * m10) * m22 + (m02 * m10 - m00 * m12) * m21 + (m01 * m12 - m02 * m11) * m20; + double nm00 = (e - i) * s, nm01 = (o - q) * s, nm02 = (k - g) * s; + double nm10 = (j - a) * s, nm11 = (r - m) * s, nm12 = (c - l) * s; + double nm20 = (b - f) * s, nm21 = (n - p) * s, nm22 = (h - d) * s; + corner.x = -nm00 - nm10 - nm20 + (a * m31 - b * m32 + f * m32 - e * m30 + i * m30 - j * m31) * s; + corner.y = -nm01 - nm11 - nm21 + (m * m31 - n * m32 + p * m32 - o * m30 + q * m30 - r * m31) * s; + corner.z = -nm02 - nm12 - nm22 + (g * m30 - k * m30 + l * m31 - c * m31 + d * m32 - h * m32) * s; + xDir.x = 2.0 * nm00; xDir.y = 2.0 * nm01; xDir.z = 2.0 * nm02; + yDir.x = 2.0 * nm10; yDir.y = 2.0 * nm11; yDir.z = 2.0 * nm12; + zDir.x = 2.0 * nm20; zDir.y = 2.0 * nm21; zDir.z = 2.0 * nm22; + return this; + } + + public boolean testPoint(double x, double y, double z) { + double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30; + double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30; + double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31; + double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31; + double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32; + double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32; + return nxX * x + nxY * y + nxZ * z + nxW >= 0 && pxX * x + pxY * y + pxZ * z + pxW >= 0 && + nyX * x + nyY * y + nyZ * z + nyW >= 0 && pyX * x + pyY * y + pyZ * z + pyW >= 0 && + nzX * x + nzY * y + nzZ * z + nzW >= 0 && pzX * x + pzY * y + pzZ * z + pzW >= 0; + } + + public boolean testSphere(double x, double y, double z, double r) { + double invl; + double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30; + invl = Math.invsqrt(nxX * nxX + nxY * nxY + nxZ * nxZ); + nxX *= invl; nxY *= invl; nxZ *= invl; nxW *= invl; + double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30; + invl = Math.invsqrt(pxX * pxX + pxY * pxY + pxZ * pxZ); + pxX *= invl; pxY *= invl; pxZ *= invl; pxW *= invl; + double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31; + invl = Math.invsqrt(nyX * nyX + nyY * nyY + nyZ * nyZ); + nyX *= invl; nyY *= invl; nyZ *= invl; nyW *= invl; + double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31; + invl = Math.invsqrt(pyX * pyX + pyY * pyY + pyZ * pyZ); + pyX *= invl; pyY *= invl; pyZ *= invl; pyW *= invl; + double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32; + invl = Math.invsqrt(nzX * nzX + nzY * nzY + nzZ * nzZ); + nzX *= invl; nzY *= invl; nzZ *= invl; nzW *= invl; + double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32; + invl = Math.invsqrt(pzX * pzX + pzY * pzY + pzZ * pzZ); + pzX *= invl; pzY *= invl; pzZ *= invl; pzW *= invl; + return nxX * x + nxY * y + nxZ * z + nxW >= -r && pxX * x + pxY * y + pxZ * z + pxW >= -r && + nyX * x + nyY * y + nyZ * z + nyW >= -r && pyX * x + pyY * y + pyZ * z + pyW >= -r && + nzX * x + nzY * y + nzZ * z + nzW >= -r && pzX * x + pzY * y + pzZ * z + pzW >= -r; + } + + public boolean testAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30; + double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30; + double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31; + double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31; + double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32; + double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32; + /* + * This is an implementation of the "2.4 Basic intersection test" of the mentioned site. + * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted. + */ + return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW && + pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW && + nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW && + pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW && + nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW && + pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @return this + */ + public Matrix4d obliqueZ(double a, double b) { + this.m20 = m00 * a + m10 * b + m20; + this.m21 = m01 * a + m11 * b + m21; + this.m22 = m02 * a + m12 * b + m22; + this.properties &= PROPERTY_AFFINE; + return this; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + public Matrix4d obliqueZ(double a, double b, Matrix4d dest) { + dest._m00(m00) + ._m01(m01) + ._m02(m02) + ._m03(m03) + ._m10(m10) + ._m11(m11) + ._m12(m12) + ._m13(m13) + ._m20(m00 * a + m10 * b + m20) + ._m21(m01 * a + m11 * b + m21) + ._m22(m02 * a + m12 * b + m22) + ._m23(m23) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & PROPERTY_AFFINE); + return dest; + } + + /** + * Create a view and projection matrix from a given eye position, a given bottom left corner position p of the near plane rectangle + * and the extents of the near plane rectangle along its local x and y axes, and store the resulting matrices + * in projDest and viewDest. + *

+ * This method creates a view and perspective projection matrix assuming that there is a pinhole camera at position eye + * projecting the scene onto the near plane defined by the rectangle. + *

+ * All positions and lengths are in the same (world) unit. + * + * @param eye + * the position of the camera + * @param p + * the bottom left corner of the near plane rectangle (will map to the bottom left corner in window coordinates) + * @param x + * the direction and length of the local "bottom/top" X axis/side of the near plane rectangle + * @param y + * the direction and length of the local "left/right" Y axis/side of the near plane rectangle + * @param nearFarDist + * the distance between the far and near plane (the near plane will be calculated by this method). + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * If the special value {@link Double#NEGATIVE_INFINITY} is used, the near and far planes will be swapped and + * the near clipping plane will be at positive infinity. + * If a negative value is used (except for {@link Double#NEGATIVE_INFINITY}) the near and far planes will be swapped + * @param zeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param projDest + * will hold the resulting projection matrix + * @param viewDest + * will hold the resulting view matrix + */ + public static void projViewFromRectangle( + Vector3d eye, Vector3d p, Vector3d x, Vector3d y, double nearFarDist, boolean zeroToOne, + Matrix4d projDest, Matrix4d viewDest) { + double zx = y.y * x.z - y.z * x.y, zy = y.z * x.x - y.x * x.z, zz = y.x * x.y - y.y * x.x; + double zd = zx * (p.x - eye.x) + zy * (p.y - eye.y) + zz * (p.z - eye.z); + double zs = zd >= 0 ? 1 : -1; zx *= zs; zy *= zs; zz *= zs; zd *= zs; + viewDest.setLookAt(eye.x, eye.y, eye.z, eye.x + zx, eye.y + zy, eye.z + zz, y.x, y.y, y.z); + double px = viewDest.m00 * p.x + viewDest.m10 * p.y + viewDest.m20 * p.z + viewDest.m30; + double py = viewDest.m01 * p.x + viewDest.m11 * p.y + viewDest.m21 * p.z + viewDest.m31; + double tx = viewDest.m00 * x.x + viewDest.m10 * x.y + viewDest.m20 * x.z; + double ty = viewDest.m01 * y.x + viewDest.m11 * y.y + viewDest.m21 * y.z; + double len = Math.sqrt(zx * zx + zy * zy + zz * zz); + double near = zd / len, far; + if (Double.isInfinite(nearFarDist) && nearFarDist < 0.0) { + far = near; + near = Double.POSITIVE_INFINITY; + } else if (Double.isInfinite(nearFarDist) && nearFarDist > 0.0) { + far = Double.POSITIVE_INFINITY; + } else if (nearFarDist < 0.0) { + far = near; + near = near + nearFarDist; + } else { + far = near + nearFarDist; + } + projDest.setFrustum(px, px + tx, py, py + ty, near, far, zeroToOne); + } + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3d)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3d)}) and the + * given vector up. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from + * {@link #setLookAt(Vector3dc, Vector3dc, Vector3dc)} called with the current + * local origin of this matrix (as obtained by {@link #originAffine(Vector3d)}), the sum of this position and the + * negated local Z axis as well as the given vector up. + *

+ * This method must only be called on {@link #isAffine()} matrices. + * + * @param up + * the up vector + * @return this + */ + public Matrix4d withLookAtUp(Vector3dc up) { + return withLookAtUp(up.x(), up.y(), up.z(), this); + } + + public Matrix4d withLookAtUp(Vector3dc up, Matrix4d dest) { + return withLookAtUp(up.x(), up.y(), up.z()); + } + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3d)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3d)}) and the + * given vector (upX, upY, upZ). + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from + * {@link #setLookAt(double, double, double, double, double, double, double, double, double)} called with the current + * local origin of this matrix (as obtained by {@link #originAffine(Vector3d)}), the sum of this position and the + * negated local Z axis as well as the given vector (upX, upY, upZ). + *

+ * This method must only be called on {@link #isAffine()} matrices. + * + * @param upX + * the x coordinate of the up vector + * @param upY + * the y coordinate of the up vector + * @param upZ + * the z coordinate of the up vector + * @return this + */ + public Matrix4d withLookAtUp(double upX, double upY, double upZ) { + return withLookAtUp(upX, upY, upZ, this); + } + + public Matrix4d withLookAtUp(double upX, double upY, double upZ, Matrix4d dest) { + double y = (upY * m21 - upZ * m11) * m02 + + (upZ * m01 - upX * m21) * m12 + + (upX * m11 - upY * m01) * m22; + double x = upX * m01 + upY * m11 + upZ * m21; + if ((properties & PROPERTY_ORTHONORMAL) == 0) + x *= Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21); + double invsqrt = Math.invsqrt(y * y + x * x); + double c = x * invsqrt, s = y * invsqrt; + double nm00 = c * m00 - s * m01, nm10 = c * m10 - s * m11, nm20 = c * m20 - s * m21, nm31 = s * m30 + c * m31; + double nm01 = s * m00 + c * m01, nm11 = s * m10 + c * m11, nm21 = s * m20 + c * m21, nm30 = c * m30 - s * m31; + dest._m00(nm00)._m10(nm10)._m20(nm20)._m30(nm30) + ._m01(nm01)._m11(nm11)._m21(nm21)._m31(nm31); + if (dest != this) { + dest + ._m02(m02)._m12(m12)._m22(m22)._m32(m32) + ._m03(m03)._m13(m13)._m23(m23)._m33(m33); + } + dest._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Multiply this by the matrix + *

+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 1 0 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapXZY() { + return mapXZY(this); + } + public Matrix4d mapXZY(Matrix4d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapXZnY() { + return mapXZnY(this); + } + public Matrix4d mapXZnY(Matrix4d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapXnYnZ() { + return mapXnYnZ(this); + } + public Matrix4d mapXnYnZ(Matrix4d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapXnZY() { + return mapXnZY(this); + } + public Matrix4d mapXnZY(Matrix4d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapXnZnY() { + return mapXnZnY(this); + } + public Matrix4d mapXnZnY(Matrix4d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapYXZ() { + return mapYXZ(this); + } + public Matrix4d mapYXZ(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapYXnZ() { + return mapYXnZ(this); + } + public Matrix4d mapYXnZ(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 1 0 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapYZX() { + return mapYZX(this); + } + public Matrix4d mapYZX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapYZnX() { + return mapYZnX(this); + } + public Matrix4d mapYZnX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapYnXZ() { + return mapYnXZ(this); + } + public Matrix4d mapYnXZ(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapYnXnZ() { + return mapYnXnZ(this); + } + public Matrix4d mapYnXnZ(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapYnZX() { + return mapYnZX(this); + } + public Matrix4d mapYnZX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapYnZnX() { + return mapYnZnX(this); + } + public Matrix4d mapYnZnX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapZXY() { + return mapZXY(this); + } + public Matrix4d mapZXY(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapZXnY() { + return mapZXnY(this); + } + public Matrix4d mapZXnY(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapZYX() { + return mapZYX(this); + } + public Matrix4d mapZYX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapZYnX() { + return mapZYnX(this); + } + public Matrix4d mapZYnX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapZnXY() { + return mapZnXY(this); + } + public Matrix4d mapZnXY(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapZnXnY() { + return mapZnXnY(this); + } + public Matrix4d mapZnXnY(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapZnYX() { + return mapZnYX(this); + } + public Matrix4d mapZnYX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapZnYnX() { + return mapZnYnX(this); + } + public Matrix4d mapZnYnX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0 -1 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnXYnZ() { + return mapnXYnZ(this); + } + public Matrix4d mapnXYnZ(Matrix4d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 1 0 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnXZY() { + return mapnXZY(this); + } + public Matrix4d mapnXZY(Matrix4d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 1  0 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnXZnY() { + return mapnXZnY(this); + } + public Matrix4d mapnXZnY(Matrix4d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 1 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnXnYZ() { + return mapnXnYZ(this); + } + public Matrix4d mapnXnYZ(Matrix4d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0 -1 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnXnYnZ() { + return mapnXnYnZ(this); + } + public Matrix4d mapnXnYnZ(Matrix4d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0 -1 0 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnXnZY() { + return mapnXnZY(this); + } + public Matrix4d mapnXnZY(Matrix4d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0 -1  0 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnXnZnY() { + return mapnXnZnY(this); + } + public Matrix4d mapnXnZnY(Matrix4d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnYXZ() { + return mapnYXZ(this); + } + public Matrix4d mapnYXZ(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnYXnZ() { + return mapnYXnZ(this); + } + public Matrix4d mapnYXnZ(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnYZX() { + return mapnYZX(this); + } + public Matrix4d mapnYZX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnYZnX() { + return mapnYZnX(this); + } + public Matrix4d mapnYZnX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnYnXZ() { + return mapnYnXZ(this); + } + public Matrix4d mapnYnXZ(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnYnXnZ() { + return mapnYnXnZ(this); + } + public Matrix4d mapnYnXnZ(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnYnZX() { + return mapnYnZX(this); + } + public Matrix4d mapnYnZX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnYnZnX() { + return mapnYnZnX(this); + } + public Matrix4d mapnYnZnX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnZXY() { + return mapnZXY(this); + } + public Matrix4d mapnZXY(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnZXnY() { + return mapnZXnY(this); + } + public Matrix4d mapnZXnY(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnZYX() { + return mapnZYX(this); + } + public Matrix4d mapnZYX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnZYnX() { + return mapnZYnX(this); + } + public Matrix4d mapnZYnX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnZnXY() { + return mapnZnXY(this); + } + public Matrix4d mapnZnXY(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnZnXnY() { + return mapnZnXnY(this); + } + public Matrix4d mapnZnXnY(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnZnYX() { + return mapnZnYX(this); + } + public Matrix4d mapnZnYX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d mapnZnYnX() { + return mapnZnYnX(this); + } + public Matrix4d mapnZnYnX(Matrix4d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 1 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d negateX() { + return _m00(-m00)._m01(-m01)._m02(-m02); + } + public Matrix4d negateX(Matrix4d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33); + } + + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4d negateY() { + return _m10(-m10)._m11(-m11)._m12(-m12); + } + public Matrix4d negateY(Matrix4d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33); + } + + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4d negateZ() { + return _m20(-m20)._m21(-m21)._m22(-m22); + } + public Matrix4d negateZ(Matrix4d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33); + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && Math.isFinite(m03) && + Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && Math.isFinite(m13) && + Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22) && Math.isFinite(m23) && + Math.isFinite(m30) && Math.isFinite(m31) && Math.isFinite(m32) && Math.isFinite(m33); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4dStack.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4dStack.java new file mode 100644 index 000000000..7b913198e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4dStack.java @@ -0,0 +1,185 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A stack of many {@link Matrix4d} instances. This resembles the matrix stack known from legacy OpenGL. + *

+ * This {@link Matrix4dStack} class inherits from {@link Matrix4d}, so the current/top matrix is always the {@link Matrix4dStack}/{@link Matrix4d} itself. This + * affects all operations in {@link Matrix4d} that take another {@link Matrix4d} as parameter. If a {@link Matrix4dStack} is used as argument to those methods, + * the effective argument will always be the current matrix of the matrix stack. + * + * @author Kai Burjack + */ +public class Matrix4dStack extends Matrix4d { + + private static final long serialVersionUID = 1L; + + /** + * The matrix stack as a non-growable array. The size of the stack must be specified in the {@link #Matrix4dStack(int) constructor}. + */ + private Matrix4d[] mats; + + /** + * The index of the "current" matrix within {@link #mats}. + */ + private int curr; + + /** + * Create a new {@link Matrix4dStack} of the given size. + *

+ * Initially the stack pointer is at zero and the current matrix is set to identity. + * + * @param stackSize + * the size of the stack. This must be at least 1, in which case the {@link Matrix4dStack} simply only consists of this + * {@link Matrix4d} + */ + public Matrix4dStack(int stackSize) { + if (stackSize < 1) { + throw new IllegalArgumentException("stackSize must be >= 1"); //$NON-NLS-1$ + } + mats = new Matrix4d[stackSize - 1]; + // Allocate all matrices up front to keep the promise of being "allocation-free" + for (int i = 0; i < mats.length; i++) { + mats[i] = new Matrix4d(); + } + } + + /** + * Do not invoke manually! Only meant for serialization. + *

+ * Invoking this constructor from client code will result in an inconsistent state of the + * created {@link Matrix4dStack} instance. + */ + public Matrix4dStack() { + /* Empty! */ + } + + /** + * Set the stack pointer to zero and set the current/bottom matrix to {@link #identity() identity}. + * + * @return this + */ + public Matrix4dStack clear() { + curr = 0; + identity(); + return this; + } + + /** + * Increment the stack pointer by one and set the values of the new current matrix to the one directly below it. + * + * @return this + */ + public Matrix4dStack pushMatrix() { + if (curr == mats.length) { + throw new IllegalStateException("max stack size of " + (curr + 1) + " reached"); //$NON-NLS-1$ //$NON-NLS-2$ + } + mats[curr++].set(this); + return this; + } + + /** + * Decrement the stack pointer by one. + *

+ * This will effectively dispose of the current matrix. + * + * @return this + */ + public Matrix4dStack popMatrix() { + if (curr == 0) { + throw new IllegalStateException("already at the bottom of the stack"); //$NON-NLS-1$ + } + set(mats[--curr]); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + curr; + for (int i = 0; i < curr; i++) { + result = prime * result + mats[i].hashCode(); + } + return result; + } + + /* + * Contract between Matrix4d and Matrix4dStack: + * + * - Matrix4d.equals(Matrix4dStack) is true iff all the 16 matrix elements are equal + * - Matrix4dStack.equals(Matrix4d) is true iff all the 16 matrix elements are equal + * - Matrix4dStack.equals(Matrix4dStack) is true iff all 16 matrix elements are equal AND the matrix arrays as well as the stack pointer are equal + * - everything else is inequal + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (obj instanceof Matrix4dStack) { + Matrix4dStack other = (Matrix4dStack) obj; + if (curr != other.curr) + return false; + for (int i = 0; i < curr; i++) { + if (!mats[i].equals(other.mats[i])) + return false; + } + } + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(curr); + for (int i = 0; i < curr; i++) { + out.writeObject(mats[i]); + } + } + + public void readExternal(ObjectInput in) throws IOException { + super.readExternal(in); + curr = in.readInt(); + mats = new Matrix4dStack[curr]; + for (int i = 0; i < curr; i++) { + Matrix4d m = new Matrix4d(); + m.readExternal(in); + mats[i] = m; + } + } + + public Object clone() throws CloneNotSupportedException { + Matrix4dStack cloned = (Matrix4dStack) super.clone(); + Matrix4d[] clonedMats = new Matrix4d[mats.length]; + for (int i = 0; i < mats.length; i++) + clonedMats[i] = (Matrix4d) mats[i].clone(); + cloned.mats = clonedMats; + return cloned; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4dc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4dc.java new file mode 100644 index 000000000..567ca90d4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4dc.java @@ -0,0 +1,6289 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 4x4 matrix of double-precision floats. + * + * @author Kai Burjack + */ +public interface Matrix4dc { + + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation x=-1 when using the identity matrix. + */ + int PLANE_NX = 0; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation x=1 when using the identity matrix. + */ + int PLANE_PX = 1; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation y=-1 when using the identity matrix. + */ + int PLANE_NY = 2; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation y=1 when using the identity matrix. + */ + int PLANE_PY = 3; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation z=-1 when using the identity matrix. + */ + int PLANE_NZ = 4; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation z=1 when using the identity matrix. + */ + int PLANE_PZ = 5; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)} + * identifying the corner (-1, -1, -1) when using the identity matrix. + */ + int CORNER_NXNYNZ = 0; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)} + * identifying the corner (1, -1, -1) when using the identity matrix. + */ + int CORNER_PXNYNZ = 1; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)} + * identifying the corner (1, 1, -1) when using the identity matrix. + */ + int CORNER_PXPYNZ = 2; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)} + * identifying the corner (-1, 1, -1) when using the identity matrix. + */ + int CORNER_NXPYNZ = 3; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)} + * identifying the corner (1, -1, 1) when using the identity matrix. + */ + int CORNER_PXNYPZ = 4; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)} + * identifying the corner (-1, -1, 1) when using the identity matrix. + */ + int CORNER_NXNYPZ = 5; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)} + * identifying the corner (-1, 1, 1) when using the identity matrix. + */ + int CORNER_NXPYPZ = 6; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)} + * identifying the corner (1, 1, 1) when using the identity matrix. + */ + int CORNER_PXPYPZ = 7; + + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents a perspective transformation. + */ + byte PROPERTY_PERSPECTIVE = 1<<0; + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents an affine transformation. + */ + byte PROPERTY_AFFINE = 1<<1; + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents the identity transformation. + */ + byte PROPERTY_IDENTITY = 1<<2; + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents a pure translation transformation. + */ + byte PROPERTY_TRANSLATION = 1<<3; + /** + * Bit returned by {@link #properties()} to indicate that the upper-left 3x3 submatrix represents an orthogonal + * matrix (i.e. orthonormal basis). For practical reasons, this property also always implies + * {@link #PROPERTY_AFFINE} in this implementation. + */ + byte PROPERTY_ORTHONORMAL = 1<<4; + + /** + * Return the assumed properties of this matrix. This is a bit-combination of + * {@link #PROPERTY_IDENTITY}, {@link #PROPERTY_AFFINE}, + * {@link #PROPERTY_TRANSLATION} and {@link #PROPERTY_PERSPECTIVE}. + * + * @return the properties of the matrix + */ + int properties(); + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + double m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + double m01(); + + /** + * Return the value of the matrix element at column 0 and row 2. + * + * @return the value of the matrix element + */ + double m02(); + + /** + * Return the value of the matrix element at column 0 and row 3. + * + * @return the value of the matrix element + */ + double m03(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + double m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + double m11(); + + /** + * Return the value of the matrix element at column 1 and row 2. + * + * @return the value of the matrix element + */ + double m12(); + + /** + * Return the value of the matrix element at column 1 and row 3. + * + * @return the value of the matrix element + */ + double m13(); + + /** + * Return the value of the matrix element at column 2 and row 0. + * + * @return the value of the matrix element + */ + double m20(); + + /** + * Return the value of the matrix element at column 2 and row 1. + * + * @return the value of the matrix element + */ + double m21(); + + /** + * Return the value of the matrix element at column 2 and row 2. + * + * @return the value of the matrix element + */ + double m22(); + + /** + * Return the value of the matrix element at column 2 and row 3. + * + * @return the value of the matrix element + */ + double m23(); + + /** + * Return the value of the matrix element at column 3 and row 0. + * + * @return the value of the matrix element + */ + double m30(); + + /** + * Return the value of the matrix element at column 3 and row 1. + * + * @return the value of the matrix element + */ + double m31(); + + /** + * Return the value of the matrix element at column 3 and row 2. + * + * @return the value of the matrix element + */ + double m32(); + + /** + * Return the value of the matrix element at column 3 and row 3. + * + * @return the value of the matrix element + */ + double m33(); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mul(Matrix4dc right, Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + *

+ * This method neither assumes nor checks for any matrix properties of this or right + * and will always perform a complete 4x4 matrix multiplication. This method should only be used whenever the + * multiplied matrices do not have any properties for which there are optimized multiplication methods available. + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mul0(Matrix4dc right, Matrix4d dest); + + /** + * Multiply this matrix by the matrix with the supplied elements and store the result in dest. + *

+ * If M is this matrix and R the right matrix whose + * elements are supplied via the parameters, then the new matrix will be M * R. + * So when transforming a vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param r00 + * the m00 element of the right matrix + * @param r01 + * the m01 element of the right matrix + * @param r02 + * the m02 element of the right matrix + * @param r03 + * the m03 element of the right matrix + * @param r10 + * the m10 element of the right matrix + * @param r11 + * the m11 element of the right matrix + * @param r12 + * the m12 element of the right matrix + * @param r13 + * the m13 element of the right matrix + * @param r20 + * the m20 element of the right matrix + * @param r21 + * the m21 element of the right matrix + * @param r22 + * the m22 element of the right matrix + * @param r23 + * the m23 element of the right matrix + * @param r30 + * the m30 element of the right matrix + * @param r31 + * the m31 element of the right matrix + * @param r32 + * the m32 element of the right matrix + * @param r33 + * the m33 element of the right matrix + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mul( + double r00, double r01, double r02, double r03, + double r10, double r11, double r12, double r13, + double r20, double r21, double r22, double r23, + double r30, double r31, double r32, double r33, Matrix4d dest); + + /** + * Multiply this matrix by the 3x3 matrix with the supplied elements expanded to a 4x4 matrix with + * all other matrix elements set to identity, and store the result in dest. + *

+ * If M is this matrix and R the right matrix whose + * elements are supplied via the parameters, then the new matrix will be M * R. + * So when transforming a vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param r00 + * the m00 element of the right matrix + * @param r01 + * the m01 element of the right matrix + * @param r02 + * the m02 element of the right matrix + * @param r10 + * the m10 element of the right matrix + * @param r11 + * the m11 element of the right matrix + * @param r12 + * the m12 element of the right matrix + * @param r20 + * the m20 element of the right matrix + * @param r21 + * the m21 element of the right matrix + * @param r22 + * the m22 element of the right matrix + * @param dest + * the destination matrix, which will hold the result + * @return this + */ + Matrix4d mul3x3( + double r00, double r01, double r02, + double r10, double r11, double r12, + double r20, double r21, double r22, Matrix4d dest); + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in dest. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mulLocal(Matrix4dc left, Matrix4d dest); + + /** + * Pre-multiply this matrix by the supplied left matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in dest. + *

+ * This method assumes that this matrix and the given left matrix both represent an {@link #isAffine() affine} transformation + * (i.e. their last rows are equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * This method will not modify either the last row of this or the last row of left. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mulLocalAffine(Matrix4dc left, Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mul(Matrix3x2dc right, Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mul(Matrix3x2fc right, Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * The last row of the right matrix is assumed to be (0, 0, 0, 1). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mul(Matrix4x3dc right, Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * The last row of the right matrix is assumed to be (0, 0, 0, 1). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mul(Matrix4x3fc right, Matrix4d dest); + + /** + * Multiply this matrix by the supplied parameter matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mul(Matrix4fc right, Matrix4d dest); + + /** + * Multiply this symmetric perspective projection matrix by the supplied {@link #isAffine() affine} view matrix and store the result in dest. + *

+ * If P is this matrix and V the view matrix, + * then the new matrix will be P * V. So when transforming a + * vector v with the new matrix by using P * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the {@link #isAffine() affine} matrix to multiply this symmetric perspective projection matrix by + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mulPerspectiveAffine(Matrix4dc view, Matrix4d dest); + + /** + * Multiply this symmetric perspective projection matrix by the supplied view matrix and store the result in dest. + *

+ * If P is this matrix and V the view matrix, + * then the new matrix will be P * V. So when transforming a + * vector v with the new matrix by using P * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the matrix to multiply this symmetric perspective projection matrix by + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mulPerspectiveAffine(Matrix4x3dc view, Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix, which is assumed to be {@link #isAffine() affine}, and store the result in dest. + *

+ * This method assumes that the given right matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mulAffineR(Matrix4dc right, Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in dest. + *

+ * This method assumes that this matrix and the given right matrix both represent an {@link #isAffine() affine} transformation + * (i.e. their last rows are equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mulAffine(Matrix4dc right, Matrix4d dest); + + /** + * Multiply this matrix, which is assumed to only contain a translation, by the supplied right matrix, which is assumed to be {@link #isAffine() affine}, and store the result in dest. + *

+ * This method assumes that this matrix only contains a translation, and that the given right matrix represents an {@link #isAffine() affine} transformation + * (i.e. its last row is equal to (0, 0, 0, 1)). + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mulTranslationAffine(Matrix4dc right, Matrix4d dest); + + /** + * Multiply this orthographic projection matrix by the supplied {@link #isAffine() affine} view matrix + * and store the result in dest. + *

+ * If M is this matrix and V the view matrix, + * then the new matrix will be M * V. So when transforming a + * vector v with the new matrix by using M * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the affine matrix which to multiply this with + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d mulOrthoAffine(Matrix4dc view, Matrix4d dest); + + /** + * Component-wise add the upper 4x3 submatrices of this and other + * by first multiplying each component of other's 4x3 submatrix by otherFactor, + * adding that to this and storing the final result in dest. + *

+ * The other components of dest will be set to the ones of this. + *

+ * The matrices this and other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's 4x3 components + * @param dest + * will hold the result + * @return dest + */ + Matrix4d fma4x3(Matrix4dc other, double otherFactor, Matrix4d dest); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix4d add(Matrix4dc other, Matrix4d dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix4d sub(Matrix4dc subtrahend, Matrix4d dest); + + /** + * Component-wise multiply this by other and store the result in dest. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mulComponentWise(Matrix4dc other, Matrix4d dest); + + /** + * Component-wise add the upper 4x3 submatrices of this and other + * and store the result in dest. + *

+ * The other components of dest will be set to the ones of this. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix4d add4x3(Matrix4dc other, Matrix4d dest); + + /** + * Component-wise add the upper 4x3 submatrices of this and other + * and store the result in dest. + *

+ * The other components of dest will be set to the ones of this. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix4d add4x3(Matrix4fc other, Matrix4d dest); + + /** + * Component-wise subtract the upper 4x3 submatrices of subtrahend from this + * and store the result in dest. + *

+ * The other components of dest will be set to the ones of this. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix4d sub4x3(Matrix4dc subtrahend, Matrix4d dest); + + /** + * Component-wise multiply the upper 4x3 submatrices of this by other + * and store the result in dest. + *

+ * The other components of dest will be set to the ones of this. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mul4x3ComponentWise(Matrix4dc other, Matrix4d dest); + + /** + * Return the determinant of this matrix. + *

+ * If this matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing, + * and thus its last row is equal to (0, 0, 0, 1), then {@link #determinantAffine()} can be used instead of this method. + * + * @see #determinantAffine() + * + * @return the determinant + */ + double determinant(); + + /** + * Return the determinant of the upper left 3x3 submatrix of this matrix. + * + * @return the determinant + */ + double determinant3x3(); + + /** + * Return the determinant of this matrix by assuming that it represents an {@link #isAffine() affine} transformation and thus + * its last row is equal to (0, 0, 0, 1). + * + * @return the determinant + */ + double determinantAffine(); + + /** + * Invert this matrix and store the result in dest. + *

+ * If this matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing, + * and thus its last row is equal to (0, 0, 0, 1), then {@link #invertAffine(Matrix4d)} can be used instead of this method. + * + * @see #invertAffine(Matrix4d) + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d invert(Matrix4d dest); + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(double, double, double, double, Matrix4d) perspective()} methods, + * that is, if this is a symmetrical perspective frustum transformation, + * then this method builds the inverse of this and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix when being obtained via {@link #perspective(double, double, double, double, Matrix4d) perspective()}. + * + * @see #perspective(double, double, double, double, Matrix4d) + * + * @param dest + * will hold the inverse of this + * @return dest + */ + Matrix4d invertPerspective(Matrix4d dest); + + /** + * If this is an arbitrary perspective projection matrix obtained via one of the {@link #frustum(double, double, double, double, double, double, Matrix4d) frustum()} methods, + * then this method builds the inverse of this and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix. + *

+ * If this matrix represents a symmetric perspective frustum transformation, as obtained via {@link #perspective(double, double, double, double, Matrix4d) perspective()}, then + * {@link #invertPerspective(Matrix4d)} should be used instead. + * + * @see #frustum(double, double, double, double, double, double, Matrix4d) + * @see #invertPerspective(Matrix4d) + * + * @param dest + * will hold the inverse of this + * @return dest + */ + Matrix4d invertFrustum(Matrix4d dest); + + /** + * Invert this orthographic projection matrix and store the result into the given dest. + *

+ * This method can be used to quickly obtain the inverse of an orthographic projection matrix. + * + * @param dest + * will hold the inverse of this + * @return dest + */ + Matrix4d invertOrtho(Matrix4d dest); + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(double, double, double, double, Matrix4d) perspective()} methods, + * that is, if this is a symmetrical perspective frustum transformation + * and the given view matrix is {@link #isAffine() affine} and has unit scaling (for example by being obtained via {@link #lookAt(double, double, double, double, double, double, double, double, double, Matrix4d) lookAt()}), + * then this method builds the inverse of this * view and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of the combination of the view and projection matrices, when both were obtained + * via the common methods {@link #perspective(double, double, double, double, Matrix4d) perspective()} and {@link #lookAt(double, double, double, double, double, double, double, double, double, Matrix4d) lookAt()} or + * other methods, that build affine matrices, such as {@link #translate(double, double, double, Matrix4d) translate} and {@link #rotate(double, double, double, double, Matrix4d)}, except for {@link #scale(double, double, double, Matrix4d) scale()}. + *

+ * For the special cases of the matrices this and view mentioned above, this method is equivalent to the following code: + *

+     * dest.set(this).mul(view).invert();
+     * 
+ * + * @param view + * the view transformation (must be {@link #isAffine() affine} and have unit scaling) + * @param dest + * will hold the inverse of this * view + * @return dest + */ + Matrix4d invertPerspectiveView(Matrix4dc view, Matrix4d dest); + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(double, double, double, double, Matrix4d) perspective()} methods, + * that is, if this is a symmetrical perspective frustum transformation + * and the given view matrix has unit scaling, + * then this method builds the inverse of this * view and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of the combination of the view and projection matrices, when both were obtained + * via the common methods {@link #perspective(double, double, double, double, Matrix4d) perspective()} and {@link #lookAt(double, double, double, double, double, double, double, double, double, Matrix4d) lookAt()} or + * other methods, that build affine matrices, such as {@link #translate(double, double, double, Matrix4d) translate} and {@link #rotate(double, double, double, double, Matrix4d)}, except for {@link #scale(double, double, double, Matrix4d) scale()}. + *

+ * For the special cases of the matrices this and view mentioned above, this method is equivalent to the following code: + *

+     * dest.set(this).mul(view).invert();
+     * 
+ * + * @param view + * the view transformation (must have unit scaling) + * @param dest + * will hold the inverse of this * view + * @return dest + */ + Matrix4d invertPerspectiveView(Matrix4x3dc view, Matrix4d dest); + + /** + * Invert this matrix by assuming that it is an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and write the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d invertAffine(Matrix4d dest); + + /** + * Transpose this matrix and store the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d transpose(Matrix4d dest); + + /** + * Transpose only the upper left 3x3 submatrix of this matrix and store the result in dest. + *

+ * All other matrix elements are left unchanged. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d transpose3x3(Matrix4d dest); + + /** + * Transpose only the upper left 3x3 submatrix of this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d transpose3x3(Matrix3d dest); + + /** + * Get only the translation components (m30, m31, m32) of this matrix and store them in the given vector xyz. + * + * @param dest + * will hold the translation components of this matrix + * @return dest + */ + Vector3d getTranslation(Vector3d dest); + + /** + * Get the scaling factors of this matrix for the three base axes. + * + * @param dest + * will hold the scaling factors for x, y and z + * @return dest + */ + Vector3d getScale(Vector3d dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4d get(Matrix4d dest); + + /** + * Get the current values of the upper 4x3 submatrix of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4x3d get4x3(Matrix4x3d dest); + + /** + * Get the current values of the upper left 3x3 submatrix of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3d get3x3(Matrix3d dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the first three column vectors of the upper left 3x3 submatrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaternionf#setFromUnnormalized(Matrix4dc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getUnnormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the first three column vectors of the upper left 3x3 submatrix are normalized. + * + * @see Quaternionf#setFromNormalized(Matrix4dc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getNormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the first three column vectors of the upper left 3x3 submatrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaterniond#setFromUnnormalized(Matrix4dc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getUnnormalizedRotation(Quaterniond dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the first three column vectors of the upper left 3x3 submatrix are normalized. + * + * @see Quaterniond#setFromNormalized(Matrix4dc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getNormalizedRotation(Quaterniond dest); + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get(DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given {@link DoubleBuffer}. + * + * @param index + * the absolute position into the {@link DoubleBuffer} + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer get(int index, DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given + * FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix4dc getToAddress(long address); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getFloats(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getFloats(int, ByteBuffer) + * + * @param buffer + * will receive the elements of this matrix as float values in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getFloats(ByteBuffer buffer); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the elements of this matrix as float values in column-major order + * @return the passed in buffer + */ + ByteBuffer getFloats(int index, ByteBuffer buffer); + + /** + * Store this matrix into the supplied double array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] get(double[] arr, int offset); + + /** + * Store this matrix into the supplied double array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(double[], int)}. + * + * @see #get(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] get(double[] arr); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied float array at the given offset. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given float array. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get(float[] arr, int offset); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied float array. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given float array. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get(float[] arr); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #getTransposed(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer getTransposed(DoubleBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer getTransposed(int index, DoubleBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getTransposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getTransposed(ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer getTransposed(int index, ByteBuffer buffer); + + /** + * Store the upper 4x3 submatrix of this matrix in row-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get4x3Transposed(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x3Transposed(int, DoubleBuffer) + * + * @param buffer + * will receive the values of the upper 4x3 submatrix in row-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get4x3Transposed(DoubleBuffer buffer); + + /** + * Store the upper 4x3 submatrix of this matrix in row-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of the upper 4x3 submatrix in row-major order + * @return the passed in buffer + */ + DoubleBuffer get4x3Transposed(int index, DoubleBuffer buffer); + + /** + * Store the upper 4x3 submatrix of this matrix in row-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get4x3Transposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x3Transposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of the upper 4x3 submatrix in row-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get4x3Transposed(ByteBuffer buffer); + + /** + * Store the upper 4x3 submatrix of this matrix in row-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of the upper 4x3 submatrix in row-major order + * @return the passed in buffer + */ + ByteBuffer get4x3Transposed(int index, ByteBuffer buffer); + + /** + * Transform/multiply the given vector by this matrix and store the result in that vector. + * + * @see Vector4d#mul(Matrix4dc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4d transform(Vector4d v); + + /** + * Transform/multiply the given vector by this matrix and store the result in dest. + * + * @see Vector4d#mul(Matrix4dc, Vector4d) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4d transform(Vector4dc v, Vector4d dest); + + /** + * Transform/multiply the vector (x, y, z, w) by this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param w + * the w coordinate of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4d transform(double x, double y, double z, double w, Vector4d dest); + + /** + * Transform/multiply the given vector by the transpose of this matrix and store the result in that vector. + * + * @see Vector4d#mulTranspose(Matrix4dc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4d transformTranspose(Vector4d v); + + /** + * Transform/multiply the given vector by the transpose of this matrix and store the result in dest. + * + * @see Vector4d#mulTranspose(Matrix4dc) + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will contain the result + * @return dest + */ + Vector4d transformTranspose(Vector4dc v, Vector4d dest); + + /** + * Transform/multiply the vector (x, y, z, w) by the transpose of this matrix + * and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param w + * the w coordinate of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4d transformTranspose(double x, double y, double z, double w, Vector4d dest); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in that vector. + * + * @see Vector4d#mulProject(Matrix4dc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4d transformProject(Vector4d v); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in dest. + * + * @see Vector4d#mulProject(Matrix4dc, Vector4d) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4d transformProject(Vector4dc v, Vector4d dest); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide + * and store the x, y and z components of the + * result in dest. + * + * @see Vector3d#mulProject(Matrix4dc, Vector3d) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3d transformProject(Vector4dc v, Vector3d dest); + + /** + * Transform/multiply the vector (x, y, z, w) by this matrix, perform perspective divide and store the result in dest. + * + * @param x + * the x coordinate of the direction to transform + * @param y + * the y coordinate of the direction to transform + * @param z + * the z coordinate of the direction to transform + * @param w + * the w coordinate of the direction to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4d transformProject(double x, double y, double z, double w, Vector4d dest); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in that vector. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @see Vector3d#mulProject(Matrix4dc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3d transformProject(Vector3d v); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in dest. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @see Vector3d#mulProject(Matrix4dc, Vector3d) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3d transformProject(Vector3dc v, Vector3d dest); + + /** + * Transform/multiply the vector (x, y, z) by this matrix, perform perspective divide and store the result in dest. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3d transformProject(double x, double y, double z, Vector3d dest); + + /** + * Transform/multiply the vector (x, y, z, w) by this matrix, perform perspective divide and store + * (x, y, z) of the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param w + * the w coordinate of the vector to transform + * @param dest + * will contain the (x, y, z) components of the result + * @return dest + */ + Vector3d transformProject(double x, double y, double z, double w, Vector3d dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=1, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. This method is therefore + * not suited for perspective projection transformations as it will not save the + * w component of the transformed vector. + * For perspective projection use {@link #transform(Vector4d)} or + * {@link #transformProject(Vector3d)} when perspective divide should be applied, too. + *

+ * In order to store the result in another vector, use {@link #transformPosition(Vector3dc, Vector3d)}. + * + * @see #transformPosition(Vector3dc, Vector3d) + * @see #transform(Vector4d) + * @see #transformProject(Vector3d) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3d transformPosition(Vector3d v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=1, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. This method is therefore + * not suited for perspective projection transformations as it will not save the + * w component of the transformed vector. + * For perspective projection use {@link #transform(Vector4dc, Vector4d)} or + * {@link #transformProject(Vector3dc, Vector3d)} when perspective divide should be applied, too. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector3d)}. + * + * @see #transformPosition(Vector3d) + * @see #transform(Vector4dc, Vector4d) + * @see #transformProject(Vector3dc, Vector3d) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPosition(Vector3dc v, Vector3d dest); + + /** + * Transform/multiply the 3D-vector (x, y, z), as if it was a 4D-vector with w=1, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. This method is therefore + * not suited for perspective projection transformations as it will not save the + * w component of the transformed vector. + * For perspective projection use {@link #transform(double, double, double, double, Vector4d)} or + * {@link #transformProject(double, double, double, Vector3d)} when perspective divide should be applied, too. + * + * @see #transform(double, double, double, double, Vector4d) + * @see #transformProject(double, double, double, Vector3d) + * + * @param x + * the x coordinate of the position + * @param y + * the y coordinate of the position + * @param z + * the z coordinate of the position + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPosition(double x, double y, double z, Vector3d dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector3dc, Vector3d)}. + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3d transformDirection(Vector3d v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector3d)}. + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformDirection(Vector3dc v, Vector3d dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector3fc, Vector3f)}. + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3f transformDirection(Vector3f v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector3f)}. + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformDirection(Vector3fc v, Vector3f dest); + + /** + * Transform/multiply the 3D-vector (x, y, z), as if it was a 4D-vector with w=0, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + * + * @param x + * the x coordinate of the direction to transform + * @param y + * the y coordinate of the direction to transform + * @param z + * the z coordinate of the direction to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformDirection(double x, double y, double z, Vector3d dest); + + /** + * Transform/multiply the 3D-vector (x, y, z), as if it was a 4D-vector with w=0, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + * + * @param x + * the x coordinate of the direction to transform + * @param y + * the y coordinate of the direction to transform + * @param z + * the z coordinate of the direction to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformDirection(double x, double y, double z, Vector3f dest); + + /** + * Transform/multiply the given 4D-vector by assuming that this matrix represents an {@link #isAffine() affine} transformation + * (i.e. its last row is equal to (0, 0, 0, 1)). + *

+ * In order to store the result in another vector, use {@link #transformAffine(Vector4dc, Vector4d)}. + * + * @see #transformAffine(Vector4dc, Vector4d) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4d transformAffine(Vector4d v); + + /** + * Transform/multiply the given 4D-vector by assuming that this matrix represents an {@link #isAffine() affine} transformation + * (i.e. its last row is equal to (0, 0, 0, 1)) and store the result in dest. + *

+ * In order to store the result in the same vector, use {@link #transformAffine(Vector4d)}. + * + * @see #transformAffine(Vector4d) + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformAffine(Vector4dc v, Vector4d dest); + + /** + * Transform/multiply the 4D-vector (x, y, z, w) by assuming that this matrix represents an {@link #isAffine() affine} transformation + * (i.e. its last row is equal to (0, 0, 0, 1)) and store the result in dest. + * + * @param x + * the x coordinate of the direction to transform + * @param y + * the y coordinate of the direction to transform + * @param z + * the z coordinate of the direction to transform + * @param w + * the w coordinate of the direction to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformAffine(double x, double y, double z, double w, Vector4d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix4d scale(Vector3dc xyz, Matrix4d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix4d scale(double x, double y, double z, Matrix4d dest); + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(double, double, double, Matrix4d) + * + * @param xyz + * the factor for all components + * @param dest + * will hold the result + * @return dest + */ + Matrix4d scale(double xyz, Matrix4d dest); + + /** + * Apply scaling to this matrix by by scaling the X axis by x and the Y axis by y + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix4d scaleXY(double x, double y, Matrix4d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).scale(sx, sy, sz).translate(-ox, -oy, -oz) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz, Matrix4d dest); + + /** + * Apply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).scale(factor).translate(-ox, -oy, -oz) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix4d scaleAround(double factor, double ox, double oy, double oz, Matrix4d dest); + + /** + * Pre-multiply scaling to this matrix by scaling all base axes by the given xyz factor, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param xyz + * the factor to scale all three base axes by + * @param dest + * will hold the result + * @return dest + */ + Matrix4d scaleLocal(double xyz, Matrix4d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix4d scaleLocal(double x, double y, double z, Matrix4d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using the given (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix4d().translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz).mul(this, dest) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz, Matrix4d dest); + + /** + * Pre-multiply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix4d().translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz).mul(this, dest) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz, Matrix4d dest); + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the given axis specified as x, y and z components and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + * + * @param ang + * the angle is in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotate(double ang, double x, double y, double z, Matrix4d dest); + + /** + * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateTranslation(double ang, double x, double y, double z, Matrix4d dest); + + /** + * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAffine(double ang, double x, double y, double z, Matrix4d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this {@link #isAffine() affine} + * matrix while using (ox, oy, oz) as the rotation origin, and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is only applicable if this is an {@link #isAffine() affine} matrix. + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAroundAffine(Quaterniondc quat, double ox, double oy, double oz, Matrix4d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix while using (ox, oy, oz) as the rotation origin, + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAround(Quaterniondc quat, double ox, double oy, double oz, Matrix4d dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateLocal(double ang, double x, double y, double z, Matrix4d dest); + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateLocalX(double ang, Matrix4d dest); + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateLocalY(double ang, Matrix4d dest); + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateLocalZ(double ang, Matrix4d dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix while using (ox, oy, oz) + * as the rotation origin, and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * This method is equivalent to calling: translateLocal(-ox, -oy, -oz, dest).rotateLocal(quat).translateLocal(ox, oy, oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAroundLocal(Quaterniondc quat, double ox, double oy, double oz, Matrix4d dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4d translate(Vector3dc offset, Matrix4d dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4d translate(Vector3fc offset, Matrix4d dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + Matrix4d translate(double x, double y, double z, Matrix4d dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4d translateLocal(Vector3fc offset, Matrix4d dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4d translateLocal(Vector3dc offset, Matrix4d dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + Matrix4d translateLocal(double x, double y, double z, Matrix4d dest); + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateX(double ang, Matrix4d dest); + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateY(double ang, Matrix4d dest); + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateZ(double ang, Matrix4d dest); + + /** + * Apply rotation about the Z axis to align the local +X towards (dirX, dirY) and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * The vector (dirX, dirY) must be a unit vector. + * + * @param dirX + * the x component of the normalized direction + * @param dirY + * the y component of the normalized direction + * @param dest + * will hold the result + * @return this + */ + Matrix4d rotateTowardsXY(double dirX, double dirY, Matrix4d dest); + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX, dest).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateXYZ(double angleX, double angleY, double angleZ, Matrix4d dest); + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ, Matrix4d dest); + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ, dest).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateZYX(double angleZ, double angleY, double angleX, Matrix4d dest); + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX, Matrix4d dest); + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY, dest).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateYXZ(double angleY, double angleX, double angleZ, Matrix4d dest); + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ, Matrix4d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotate(Quaterniondc quat, Matrix4d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotate(Quaternionfc quat, Matrix4d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this {@link #isAffine() affine} matrix and store + * the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAffine(Quaterniondc quat, Matrix4d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateTranslation(Quaterniondc quat, Matrix4d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateTranslation(Quaternionfc quat, Matrix4d dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateLocal(Quaterniondc quat, Matrix4d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this {@link #isAffine() affine} matrix and store + * the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateAffine(Quaternionfc quat, Matrix4d dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateLocal(Quaternionfc quat, Matrix4d dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix4d) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotate(AxisAngle4f axisAngle, Matrix4d dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix4d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotate(AxisAngle4d axisAngle, Matrix4d dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix4d) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotate(double angle, Vector3dc axis, Matrix4d dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix4d) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotate(double angle, Vector3fc axis, Matrix4d dest); + + /** + * Get the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..3] + * @param dest + * will hold the row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..3] + */ + Vector4d getRow(int row, Vector4d dest) throws IndexOutOfBoundsException; + + /** + * Get the first three components of the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..3] + * @param dest + * will hold the first three row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..3] + */ + Vector3d getRow(int row, Vector3d dest) throws IndexOutOfBoundsException; + + /** + * Get the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param dest + * will hold the column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + Vector4d getColumn(int column, Vector4d dest) throws IndexOutOfBoundsException; + + /** + * Get the first three components of the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param dest + * will hold the first three column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + Vector3d getColumn(int column, Vector3d dest) throws IndexOutOfBoundsException; + + /** + * Get the matrix element value at the given column and row. + * + * @param column + * the colum index in [0..3] + * @param row + * the row index in [0..3] + * @return the element value + */ + double get(int column, int row); + + /** + * Get the matrix element value at the given row and column. + * + * @param row + * the row index in [0..3] + * @param column + * the colum index in [0..3] + * @return the element value + */ + double getRowColumn(int row, int column); + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into the upper left 3x3 submatrix of dest. + * All other values of dest will be set to identity. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d normal(Matrix4d dest); + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @see #get3x3(Matrix3d) + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d normal(Matrix3d dest); + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this + * and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d cofactor3x3(Matrix3d dest); + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this + * and store it into dest. + * All other values of dest will be set to identity. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix4d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d cofactor3x3(Matrix4d dest); + + /** + * Normalize the upper left 3x3 submatrix of this matrix and store the result in dest. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d normalize3x3(Matrix4d dest); + + /** + * Normalize the upper left 3x3 submatrix of this matrix and store the result in dest. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d normalize3x3(Matrix3d dest); + + /** + * Unproject the given window coordinates (winX, winY, winZ) by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * The depth range of winZ is assumed to be [0..1], which is also the OpenGL default. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4d)} and then the method {@link #unprojectInv(double, double, double, int[], Vector4d) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(double, double, double, int[], Vector4d) + * @see #invert(Matrix4d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param winZ + * the z-coordinate, which is the depth value in [0..1] + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector4d unproject(double winX, double winY, double winZ, int[] viewport, Vector4d dest); + + /** + * Unproject the given window coordinates (winX, winY, winZ) by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * The depth range of winZ is assumed to be [0..1], which is also the OpenGL default. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4d)} and then the method {@link #unprojectInv(double, double, double, int[], Vector3d) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(double, double, double, int[], Vector3d) + * @see #invert(Matrix4d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param winZ + * the z-coordinate, which is the depth value in [0..1] + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector3d unproject(double winX, double winY, double winZ, int[] viewport, Vector3d dest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * The depth range of winCoords.z is assumed to be [0..1], which is also the OpenGL default. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4d)} and then the method {@link #unprojectInv(double, double, double, int[], Vector4d) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(double, double, double, int[], Vector4d) + * @see #unproject(double, double, double, int[], Vector4d) + * @see #invert(Matrix4d) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector4d unproject(Vector3dc winCoords, int[] viewport, Vector4d dest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * The depth range of winCoords.z is assumed to be [0..1], which is also the OpenGL default. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4d)} and then the method {@link #unprojectInv(double, double, double, int[], Vector4d) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(double, double, double, int[], Vector4d) + * @see #unproject(double, double, double, int[], Vector4d) + * @see #invert(Matrix4d) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector3d unproject(Vector3dc winCoords, int[] viewport, Vector3d dest); + + /** + * Unproject the given 2D window coordinates (winX, winY) by this matrix using the specified viewport + * and compute the origin and the direction of the resulting ray which starts at NDC z = -1.0 and goes through NDC z = +1.0. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4d)} and then the method {@link #unprojectInvRay(double, double, int[], Vector3d, Vector3d) unprojectInvRay()} can be invoked on it. + * + * @see #unprojectInvRay(double, double, int[], Vector3d, Vector3d) + * @see #invert(Matrix4d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param originDest + * will hold the ray origin + * @param dirDest + * will hold the (unnormalized) ray direction + * @return this + */ + Matrix4d unprojectRay(double winX, double winY, int[] viewport, Vector3d originDest, Vector3d dirDest); + + /** + * Unproject the given 2D window coordinates winCoords by this matrix using the specified viewport + * and compute the origin and the direction of the resulting ray which starts at NDC z = -1.0 and goes through NDC z = +1.0. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4d)} and then the method {@link #unprojectInvRay(double, double, int[], Vector3d, Vector3d) unprojectInvRay()} can be invoked on it. + * + * @see #unprojectInvRay(double, double, int[], Vector3d, Vector3d) + * @see #unprojectRay(double, double, int[], Vector3d, Vector3d) + * @see #invert(Matrix4d) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param originDest + * will hold the ray origin + * @param dirDest + * will hold the (unnormalized) ray direction + * @return this + */ + Matrix4d unprojectRay(Vector2dc winCoords, int[] viewport, Vector3d originDest, Vector3d dirDest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(Vector3dc, int[], Vector4d) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by this matrix. + *

+ * The depth range of winCoords.z is assumed to be [0..1], which is also the OpenGL default. + * + * @see #unproject(Vector3dc, int[], Vector4d) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector4d unprojectInv(Vector3dc winCoords, int[] viewport, Vector4d dest); + + /** + * Unproject the given window coordinates (winX, winY, winZ) by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(double, double, double, int[], Vector4d) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by this matrix. + *

+ * The depth range of winZ is assumed to be [0..1], which is also the OpenGL default. + * + * @see #unproject(double, double, double, int[], Vector4d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param winZ + * the z-coordinate, which is the depth value in [0..1] + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector4d unprojectInv(double winX, double winY, double winZ, int[] viewport, Vector4d dest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(Vector3dc, int[], Vector3d) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by this matrix. + *

+ * The depth range of winCoords.z is assumed to be [0..1], which is also the OpenGL default. + * + * @see #unproject(Vector3dc, int[], Vector3d) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector3d unprojectInv(Vector3dc winCoords, int[] viewport, Vector3d dest); + + /** + * Unproject the given window coordinates (winX, winY, winZ) by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(double, double, double, int[], Vector3d) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by this matrix. + *

+ * The depth range of winZ is assumed to be [0..1], which is also the OpenGL default. + * + * @see #unproject(double, double, double, int[], Vector3d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param winZ + * the z-coordinate, which is the depth value in [0..1] + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector3d unprojectInv(double winX, double winY, double winZ, int[] viewport, Vector3d dest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport + * and compute the origin and the direction of the resulting ray which starts at NDC z = -1.0 and goes through NDC z = +1.0. + *

+ * This method differs from {@link #unprojectRay(Vector2dc, int[], Vector3d, Vector3d) unprojectRay()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + * + * @see #unprojectRay(Vector2dc, int[], Vector3d, Vector3d) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param originDest + * will hold the ray origin + * @param dirDest + * will hold the (unnormalized) ray direction + * @return this + */ + Matrix4d unprojectInvRay(Vector2dc winCoords, int[] viewport, Vector3d originDest, Vector3d dirDest); + + /** + * Unproject the given 2D window coordinates (winX, winY) by this matrix using the specified viewport + * and compute the origin and the direction of the resulting ray which starts at NDC z = -1.0 and goes through NDC z = +1.0. + *

+ * This method differs from {@link #unprojectRay(double, double, int[], Vector3d, Vector3d) unprojectRay()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + * + * @see #unprojectRay(double, double, int[], Vector3d, Vector3d) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param originDest + * will hold the ray origin + * @param dirDest + * will hold the (unnormalized) ray direction + * @return this + */ + Matrix4d unprojectInvRay(double winX, double winY, int[] viewport, Vector3d originDest, Vector3d dirDest); + + /** + * Project the given (x, y, z) position via this matrix using the specified viewport + * and store the resulting window coordinates in winCoordsDest. + *

+ * This method transforms the given coordinates by this matrix including perspective division to + * obtain normalized device coordinates, and then translates these into window coordinates by using the + * given viewport settings [x, y, width, height]. + *

+ * The depth range of the returned winCoordsDest.z will be [0..1], which is also the OpenGL default. + * + * @param x + * the x-coordinate of the position to project + * @param y + * the y-coordinate of the position to project + * @param z + * the z-coordinate of the position to project + * @param viewport + * the viewport described by [x, y, width, height] + * @param winCoordsDest + * will hold the projected window coordinates + * @return winCoordsDest + */ + Vector4d project(double x, double y, double z, int[] viewport, Vector4d winCoordsDest); + + /** + * Project the given (x, y, z) position via this matrix using the specified viewport + * and store the resulting window coordinates in winCoordsDest. + *

+ * This method transforms the given coordinates by this matrix including perspective division to + * obtain normalized device coordinates, and then translates these into window coordinates by using the + * given viewport settings [x, y, width, height]. + *

+ * The depth range of the returned winCoordsDest.z will be [0..1], which is also the OpenGL default. + * + * @param x + * the x-coordinate of the position to project + * @param y + * the y-coordinate of the position to project + * @param z + * the z-coordinate of the position to project + * @param viewport + * the viewport described by [x, y, width, height] + * @param winCoordsDest + * will hold the projected window coordinates + * @return winCoordsDest + */ + Vector3d project(double x, double y, double z, int[] viewport, Vector3d winCoordsDest); + + /** + * Project the given position via this matrix using the specified viewport + * and store the resulting window coordinates in winCoordsDest. + *

+ * This method transforms the given coordinates by this matrix including perspective division to + * obtain normalized device coordinates, and then translates these into window coordinates by using the + * given viewport settings [x, y, width, height]. + *

+ * The depth range of the returned winCoordsDest.z will be [0..1], which is also the OpenGL default. + * + * @see #project(double, double, double, int[], Vector4d) + * + * @param position + * the position to project into window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @param winCoordsDest + * will hold the projected window coordinates + * @return winCoordsDest + */ + Vector4d project(Vector3dc position, int[] viewport, Vector4d winCoordsDest); + + /** + * Project the given position via this matrix using the specified viewport + * and store the resulting window coordinates in winCoordsDest. + *

+ * This method transforms the given coordinates by this matrix including perspective division to + * obtain normalized device coordinates, and then translates these into window coordinates by using the + * given viewport settings [x, y, width, height]. + *

+ * The depth range of the returned winCoordsDest.z will be [0..1], which is also the OpenGL default. + * + * @see #project(double, double, double, int[], Vector4d) + * + * @param position + * the position to project into window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @param winCoordsDest + * will hold the projected window coordinates + * @return winCoordsDest + */ + Vector3d project(Vector3dc position, int[] viewport, Vector3d winCoordsDest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0 and store the result in dest. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4d reflect(double a, double b, double c, double d, Matrix4d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz, Matrix4d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation and a point on the plane, and store the result in dest. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation + * @param point + * a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4d reflect(Quaterniondc orientation, Vector3dc point, Matrix4d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4d reflect(Vector3dc normal, Vector3dc point, Matrix4d dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4d dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4d dest); + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, boolean, Matrix4d) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, Matrix4d dest); + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, boolean, Matrix4d) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, Matrix4d dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double, Matrix4d) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + Matrix4d ortho2D(double left, double right, double bottom, double top, Matrix4d dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double, Matrix4d) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + Matrix4d ortho2DLH(double left, double right, double bottom, double top, Matrix4d dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3dc, Vector3dc, Vector3dc, Matrix4d) lookAt} + * with eye = (0, 0, 0) and center = dir. + * + * @see #lookAlong(double, double, double, double, double, double, Matrix4d) + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc, Matrix4d) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lookAlong(Vector3dc dir, Vector3dc up, Matrix4d dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(double, double, double, double, double, double, double, double, double, Matrix4d) lookAt()} + * with eye = (0, 0, 0) and center = dir. + * + * @see #lookAt(double, double, double, double, double, double, double, double, double, Matrix4d) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix4d dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAt(double, double, double, double, double, double, double, double, double, Matrix4d) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lookAt(Vector3dc eye, Vector3dc center, Vector3dc up, Matrix4d dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc, Matrix4d) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lookAt(double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ, Matrix4d dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * This method assumes this to be a perspective transformation, obtained via + * {@link #frustum(double, double, double, double, double, double, Matrix4d) frustum()} or {@link #perspective(double, double, double, double, Matrix4d) perspective()} or + * one of their overloads. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lookAtPerspective(double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ, Matrix4d dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lookAtLH(Vector3dc eye, Vector3dc center, Vector3dc up, Matrix4d dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAtLH(Vector3dc, Vector3dc, Vector3dc, Matrix4d) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ, Matrix4d dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * This method assumes this to be a perspective transformation, obtained via + * {@link #frustumLH(double, double, double, double, double, double, Matrix4d) frustumLH()} or {@link #perspectiveLH(double, double, double, double, Matrix4d) perspectiveLH()} or + * one of their overloads. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lookAtPerspectiveLH(double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ, Matrix4d dest); + + /** + * This method is equivalent to calling: translate(w-1-2*x, h-1-2*y, 0, dest).scale(w, h, 1) + *

+ * If M is this matrix and T the created transformation matrix, + * then the new matrix will be M * T. So when transforming a + * vector v with the new matrix by using M * T * v, the + * created transformation will be applied first! + * + * @param x + * the tile's x coordinate/index (should be in [0..w)) + * @param y + * the tile's y coordinate/index (should be in [0..h)) + * @param w + * the number of tiles along the x axis + * @param h + * the number of tiles along the y axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4d tile(int x, int y, int w, int h, Matrix4d dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, Matrix4d dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, Matrix4d dest); + + /** + * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, boolean zZeroToOne); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + Matrix4d perspectiveRect(double width, double height, double zNear, double zFar); + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, Matrix4d dest); + + /** + * Apply an asymmetric off-center perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, boolean zZeroToOne); + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @return this + */ + Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar); + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, Matrix4d dest); + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4d dest); + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4d dest); + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Double#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Double#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4d dest); + + /** + * Calculate a frustum plane of this matrix, which + * can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given dest. + *

+ * Generally, this method computes the frustum plane in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * The frustum plane will be given in the form of a general plane equation: + * a*x + b*y + c*z + d = 0, where the given {@link Vector4d} components will + * hold the (a, b, c, d) values of the equation. + *

+ * The plane normal, which is (a, b, c), is directed "inwards" of the frustum. + * Any plane/point test using a*x + b*y + c*z + d therefore will yield a result greater than zero + * if the point is within the frustum (i.e. at the positive side of the frustum plane). + *

+ * For performing frustum culling, the class {@link FrustumIntersection} should be used instead of + * manually obtaining the frustum planes and testing them against points, spheres or axis-aligned boxes. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param plane + * one of the six possible planes, given as numeric constants + * {@link #PLANE_NX}, {@link #PLANE_PX}, + * {@link #PLANE_NY}, {@link #PLANE_PY}, + * {@link #PLANE_NZ} and {@link #PLANE_PZ} + * @param dest + * will hold the computed plane equation. + * The plane equation will be normalized, meaning that (a, b, c) will be a unit vector + * @return dest + */ + Vector4d frustumPlane(int plane, Vector4d dest); + + /** + * Compute the corner coordinates of the frustum defined by this matrix, which + * can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given point. + *

+ * Generally, this method computes the frustum corners in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * Reference: http://geomalgorithms.com + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param corner + * one of the eight possible corners, given as numeric constants + * {@link #CORNER_NXNYNZ}, {@link #CORNER_PXNYNZ}, {@link #CORNER_PXPYNZ}, {@link #CORNER_NXPYNZ}, + * {@link #CORNER_PXNYPZ}, {@link #CORNER_NXNYPZ}, {@link #CORNER_NXPYPZ}, {@link #CORNER_PXPYPZ} + * @param point + * will hold the resulting corner point coordinates + * @return point + */ + Vector3d frustumCorner(int corner, Vector3d point); + + /** + * Compute the eye/origin of the perspective frustum transformation defined by this matrix, + * which can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given origin. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(double, double, double, double, Matrix4d) perspective()} + * or {@link #frustum(double, double, double, double, double, double, Matrix4d) frustum()}. + *

+ * Generally, this method computes the origin in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * This method is equivalent to calling: invert(new Matrix4d()).transformProject(0, 0, -1, 0, origin) + * and in the case of an already available inverse of this matrix, the method {@link #perspectiveInvOrigin(Vector3d)} + * on the inverse of the matrix should be used instead. + *

+ * Reference: http://geomalgorithms.com + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param origin + * will hold the origin of the coordinate system before applying this + * perspective projection transformation + * @return origin + */ + Vector3d perspectiveOrigin(Vector3d origin); + + /** + * Compute the eye/origin of the inverse of the perspective frustum transformation defined by this matrix, + * which can be the inverse of a projection matrix or the inverse of a combined modelview-projection matrix, and store the result + * in the given dest. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(double, double, double, double, Matrix4d) perspective()} + * or {@link #frustum(double, double, double, double, double, double, Matrix4d) frustum()}. + *

+ * If the inverse of the modelview-projection matrix is not available, then calling {@link #perspectiveOrigin(Vector3d)} + * on the original modelview-projection matrix is preferred. + * + * @see #perspectiveOrigin(Vector3d) + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d perspectiveInvOrigin(Vector3d dest); + + /** + * Return the vertical field-of-view angle in radians of this perspective transformation matrix. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(double, double, double, double, Matrix4d) perspective()} + * or {@link #frustum(double, double, double, double, double, double, Matrix4d) frustum()}. + *

+ * For orthogonal transformations this method will return 0.0. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @return the vertical field-of-view angle in radians + */ + double perspectiveFov(); + + /** + * Extract the near clip plane distance from this perspective projection matrix. + *

+ * This method only works if this is a perspective projection matrix, for example obtained via {@link #perspective(double, double, double, double, Matrix4d)}. + * + * @return the near clip plane distance + */ + double perspectiveNear(); + + /** + * Extract the far clip plane distance from this perspective projection matrix. + *

+ * This method only works if this is a perspective projection matrix, for example obtained via {@link #perspective(double, double, double, double, Matrix4d)}. + * + * @return the far clip plane distance + */ + double perspectiveFar(); + + /** + * Obtain the direction of a ray starting at the center of the coordinate system and going + * through the near frustum plane. + *

+ * This method computes the dir vector in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * The parameters x and y are used to interpolate the generated ray direction + * from the bottom-left to the top-right frustum corners. + *

+ * For optimal efficiency when building many ray directions over the whole frustum, + * it is recommended to use this method only in order to compute the four corner rays at + * (0, 0), (1, 0), (0, 1) and (1, 1) + * and then bilinearly interpolating between them; or to use the {@link FrustumRayBuilder}. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the interpolation factor along the left-to-right frustum planes, within [0..1] + * @param y + * the interpolation factor along the bottom-to-top frustum planes, within [0..1] + * @param dir + * will hold the normalized ray direction in the local frame of the coordinate system before + * transforming to homogeneous clipping space using this matrix + * @return dir + */ + Vector3d frustumRayDir(double x, double y, Vector3d dir); + + /** + * Obtain the direction of +Z before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +Z by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4d inv = new Matrix4d(this).invert();
+     * inv.transformDirection(dir.set(0, 0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveZ(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3d positiveZ(Vector3d dir); + + /** + * Obtain the direction of +Z before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +Z by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4d inv = new Matrix4d(this).transpose();
+     * inv.transformDirection(dir.set(0, 0, 1));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3d normalizedPositiveZ(Vector3d dir); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4d inv = new Matrix4d(this).invert();
+     * inv.transformDirection(dir.set(1, 0, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3d positiveX(Vector3d dir); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4d inv = new Matrix4d(this).transpose();
+     * inv.transformDirection(dir.set(1, 0, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3d normalizedPositiveX(Vector3d dir); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4d inv = new Matrix4d(this).invert();
+     * inv.transformDirection(dir.set(0, 1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3d positiveY(Vector3d dir); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4d inv = new Matrix4d(this).transpose();
+     * inv.transformDirection(dir.set(0, 1, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3d normalizedPositiveY(Vector3d dir); + + /** + * Obtain the position that gets transformed to the origin by this {@link #isAffine() affine} matrix. + * This can be used to get the position of the "camera" from a given view transformation matrix. + *

+ * This method only works with {@link #isAffine() affine} matrices. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).invertAffine();
+     * inv.transformPosition(origin.set(0, 0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + Vector3d originAffine(Vector3d origin); + + /** + * Obtain the position that gets transformed to the origin by this matrix. + * This can be used to get the position of the "camera" from a given view/projection transformation matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).invert();
+     * inv.transformPosition(origin.set(0, 0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + Vector3d origin(Vector3d origin); + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction light + * and store the result in dest. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param light + * the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4d shadow(Vector4dc light, double a, double b, double c, double d, Matrix4d dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW) + * and store the result in dest. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param lightX + * the x-component of the light's vector + * @param lightY + * the y-component of the light's vector + * @param lightZ + * the z-component of the light's vector + * @param lightW + * the w-component of the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d, Matrix4d dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction light + * and store the result in dest. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param light + * the light's vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @param dest + * will hold the result + * @return dest + */ + Matrix4d shadow(Vector4dc light, Matrix4dc planeTransform, Matrix4d dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW) + * and store the result in dest. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param lightX + * the x-component of the light vector + * @param lightY + * the y-component of the light vector + * @param lightZ + * the z-component of the light vector + * @param lightW + * the w-component of the light vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @param dest + * will hold the result + * @return dest + */ + Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4dc planeTransform, Matrix4d dest); + + /** + * Apply a picking transformation to this matrix using the given window coordinates (x, y) as the pick center + * and the given (width, height) as the size of the picking region in window coordinates, and store the result + * in dest. + * + * @param x + * the x coordinate of the picking region center in window coordinates + * @param y + * the y coordinate of the picking region center in window coordinates + * @param width + * the width of the picking region in window coordinates + * @param height + * the height of the picking region in window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4d pick(double x, double y, double width, double height, int[] viewport, Matrix4d dest); + + /** + * Determine whether this matrix describes an affine transformation. This is the case iff its last row is equal to (0, 0, 0, 1). + * + * @return true iff this matrix is affine; false otherwise + */ + boolean isAffine(); + + /** + * Apply an arcball view transformation to this matrix with the given radius and center (centerX, centerY, centerZ) + * position of the arcball and the specified X and Y rotation angles, and store the result in dest. + *

+ * This method is equivalent to calling: translate(0, 0, -radius, dest).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ) + * + * @param radius + * the arcball radius + * @param centerX + * the x coordinate of the center position of the arcball + * @param centerY + * the y coordinate of the center position of the arcball + * @param centerZ + * the z coordinate of the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY, Matrix4d dest); + + /** + * Apply an arcball view transformation to this matrix with the given radius and center + * position of the arcball and the specified X and Y rotation angles, and store the result in dest. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z) + * + * @param radius + * the arcball radius + * @param center + * the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4d arcball(double radius, Vector3dc center, double angleX, double angleY, Matrix4d dest); + + /** + * Compute the range matrix for the Projected Grid transformation as described in chapter "2.4.2 Creating the range conversion matrix" + * of the paper Real-time water rendering - Introducing the projected grid concept + * based on the inverse of the view-projection matrix which is assumed to be this, and store that range matrix into dest. + *

+ * If the projected grid will not be visible then this method returns null. + *

+ * This method uses the y = 0 plane for the projection. + * + * @param projector + * the projector view-projection transformation + * @param sLower + * the lower (smallest) Y-coordinate which any transformed vertex might have while still being visible on the projected grid + * @param sUpper + * the upper (highest) Y-coordinate which any transformed vertex might have while still being visible on the projected grid + * @param dest + * will hold the resulting range matrix + * @return the computed range matrix; or null if the projected grid will not be visible + */ + Matrix4d projectedGridRange(Matrix4dc projector, double sLower, double sUpper, Matrix4d dest); + + /** + * Change the near and far clip plane distances of this perspective frustum transformation matrix + * and store the result in dest. + *

+ * This method only works if this is a perspective projection frustum transformation, for example obtained + * via {@link #perspective(double, double, double, double, Matrix4d) perspective()} or {@link #frustum(double, double, double, double, double, double, Matrix4d) frustum()}. + * + * @see #perspective(double, double, double, double, Matrix4d) + * @see #frustum(double, double, double, double, double, double, Matrix4d) + * + * @param near + * the new near clip plane distance + * @param far + * the new far clip plane distance + * @param dest + * will hold the resulting matrix + * @return dest + */ + Matrix4d perspectiveFrustumSlice(double near, double far, Matrix4d dest); + + /** + * Build an ortographic projection transformation that fits the view-projection transformation represented by this + * into the given affine view transformation. + *

+ * The transformation represented by this must be given as the {@link #invert(Matrix4d) inverse} of a typical combined camera view-projection + * transformation, whose projection can be either orthographic or perspective. + *

+ * The view must be an {@link #isAffine() affine} transformation which in the application of Cascaded Shadow Maps is usually the light view transformation. + * It be obtained via any affine transformation or for example via {@link #lookAt(double, double, double, double, double, double, double, double, double, Matrix4d) lookAt()}. + *

+ * Reference: OpenGL SDK - Cascaded Shadow Maps + * + * @param view + * the view transformation to build a corresponding orthographic projection to fit the frustum of this + * @param dest + * will hold the crop projection transformation + * @return dest + */ + Matrix4d orthoCrop(Matrix4dc view, Matrix4d dest); + + /** + * Transform the axis-aligned box given as the minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ) + * by this {@link #isAffine() affine} matrix and compute the axis-aligned box of the result whose minimum corner is stored in outMin + * and maximum corner stored in outMax. + *

+ * Reference: http://dev.theomader.com + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the y coordinate of the maximum corner of the axis-aligned box + * @param outMin + * will hold the minimum corner of the resulting axis-aligned box + * @param outMax + * will hold the maximum corner of the resulting axis-aligned box + * @return this + */ + Matrix4d transformAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vector3d outMin, Vector3d outMax); + + /** + * Transform the axis-aligned box given as the minimum corner min and maximum corner max + * by this {@link #isAffine() affine} matrix and compute the axis-aligned box of the result whose minimum corner is stored in outMin + * and maximum corner stored in outMax. + * + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param outMin + * will hold the minimum corner of the resulting axis-aligned box + * @param outMax + * will hold the maximum corner of the resulting axis-aligned box + * @return this + */ + Matrix4d transformAab(Vector3dc min, Vector3dc max, Vector3d outMin, Vector3d outMax); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Matrix4d lerp(Matrix4dc other, double t, Matrix4d dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with direction + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine(), dest) + * + * @see #rotateTowards(double, double, double, double, double, double, Matrix4d) + * + * @param direction + * the direction to rotate towards + * @param up + * the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateTowards(Vector3dc direction, Vector3dc up, Matrix4d dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine(), dest) + * + * @see #rotateTowards(Vector3dc, Vector3dc, Matrix4d) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix4d dest); + + /** + * Extract the Euler angles from the rotation represented by the upper left 3x3 submatrix of this + * and store the extracted Euler angles in dest. + *

+ * This method assumes that the upper left of this only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3d#x} field, the angle around Y in the {@link Vector3d#y} + * field and the angle around Z in the {@link Vector3d#z} field of the supplied {@link Vector3d} instance. + *

+ * Note that the returned Euler angles must be applied in the order X * Y * Z to obtain the identical matrix. + * This means that calling {@link Matrix4dc#rotateXYZ(double, double, double, Matrix4d)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix4d m = ...; // <- matrix only representing rotation
+     * Matrix4d n = new Matrix4d();
+     * n.rotateXYZ(m.getEulerAnglesXYZ(new Vector3d()));
+     * 
+ *

+ * Reference: http://en.wikipedia.org/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3d getEulerAnglesXYZ(Vector3d dest); + + /** + * Extract the Euler angles from the rotation represented by the upper left 3x3 submatrix of this + * and store the extracted Euler angles in dest. + *

+ * This method assumes that the upper left of this only represents a rotation without scaling. + *

+ * Note that the returned Euler angles must be applied in the order Z * Y * X to obtain the identical matrix. + * This means that calling {@link Matrix4dc#rotateZYX(double, double, double, Matrix4d)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix4d m = ...; // <- matrix only representing rotation
+     * Matrix4d n = new Matrix4d();
+     * n.rotateZYX(m.getEulerAnglesZYX(new Vector3d()));
+     * 
+ *

+ * Reference: http://nghiaho.com/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3d getEulerAnglesZYX(Vector3d dest); + + /** + * Test whether the given point (x, y, z) is within the frustum defined by this matrix. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given point with the coordinates (x, y, z) given + * in space M is within the clip space. + *

+ * When testing multiple points using the same transformation matrix, {@link FrustumIntersection} should be used instead. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the x-coordinate of the point + * @param y + * the y-coordinate of the point + * @param z + * the z-coordinate of the point + * @return true if the given point is inside the frustum; false otherwise + */ + boolean testPoint(double x, double y, double z); + + /** + * Test whether the given sphere is partly or completely within or outside of the frustum defined by this matrix. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given sphere with the coordinates (x, y, z) given + * in space M is within the clip space. + *

+ * When testing multiple spheres using the same transformation matrix, or more sophisticated/optimized intersection algorithms are required, + * {@link FrustumIntersection} should be used instead. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for spheres that are actually not visible. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the x-coordinate of the sphere's center + * @param y + * the y-coordinate of the sphere's center + * @param z + * the z-coordinate of the sphere's center + * @param r + * the sphere's radius + * @return true if the given sphere is partly or completely inside the frustum; false otherwise + */ + boolean testSphere(double x, double y, double z, double r); + + /** + * Test whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this matrix. + * The box is specified via its min and max corner coordinates. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given axis-aligned box with its minimum corner coordinates (minX, minY, minZ) + * and maximum corner coordinates (maxX, maxY, maxZ) given in space M is within the clip space. + *

+ * When testing multiple axis-aligned boxes using the same transformation matrix, or more sophisticated/optimized intersection algorithms are required, + * {@link FrustumIntersection} should be used instead. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns -1 for boxes that are actually not visible/do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: Efficient View Frustum Culling + *
+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param minZ + * the z-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @param maxZ + * the z-coordinate of the maximum corner + * @return true if the axis-aligned box is completely or partly inside of the frustum; false otherwise + */ + boolean testAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ); + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + Matrix4d obliqueZ(double a, double b, Matrix4d dest); + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3d)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3d)}) and the + * given vector up, and store the result in dest. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from calling + * {@link Matrix4d#setLookAt(Vector3dc, Vector3dc, Vector3dc)} with the current + * local origin of this matrix (as obtained by {@link #originAffine(Vector3d)}), the sum of this position and the + * negated local Z axis as well as the given vector up. + *

+ * This method must only be called on {@link #isAffine()} matrices. + * + * @param up + * the up vector + * @param dest + * will hold the result + * @return this + */ + Matrix4d withLookAtUp(Vector3dc up, Matrix4d dest); + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3d)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3d)}) and the + * given vector (upX, upY, upZ), and store the result in dest. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from calling + * {@link Matrix4d#setLookAt(double, double, double, double, double, double, double, double, double)} called with the current + * local origin of this matrix (as obtained by {@link #originAffine(Vector3d)}), the sum of this position and the + * negated local Z axis as well as the given vector (upX, upY, upZ). + *

+ * This method must only be called on {@link #isAffine()} matrices. + * + * @param upX + * the x coordinate of the up vector + * @param upY + * the y coordinate of the up vector + * @param upZ + * the z coordinate of the up vector + * @param dest + * will hold the result + * @return this + */ + Matrix4d withLookAtUp(double upX, double upY, double upZ, Matrix4d dest); + + /** + * Multiply this by the matrix + *

+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 1 0 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapXZY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapXZnY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapXnYnZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapXnZY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapXnZnY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapYXZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapYXnZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 1 0 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapYZX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapYZnX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapYnXZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapYnXnZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapYnZX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapYnZnX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapZXY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapZXnY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapZYX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapZYnX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapZnXY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapZnXnY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapZnYX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapZnYnX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0 -1 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnXYnZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 1 0 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnXZY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 1  0 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnXZnY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 1 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnXnYZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0 -1 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnXnYnZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0 -1 0 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnXnZY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0 -1  0 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnXnZnY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnYXZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnYXnZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnYZX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnYZnX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnYnXZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnYnXnZ(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnYnZX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnYnZnX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnZXY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnZXnY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnZYX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnZYnX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnZnXY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnZnXnY(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnZnYX(Matrix4d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d mapnZnYnX(Matrix4d dest); + + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 1 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d negateX(Matrix4d dest); + + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d negateY(Matrix4d dest); + + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4d negateZ(Matrix4d dest); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix4dc m, double delta); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4f.java new file mode 100644 index 000000000..85e92cceb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4f.java @@ -0,0 +1,15355 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + + +/** + * Contains the definition of a 4x4 matrix of floats, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10 m20 m30
+ * m01 m11 m21 m31
+ * m02 m12 m22 m32
+ * m03 m13 m23 m33
+ * + * @author Richard Greenlees + * @author Kai Burjack + */ +public class Matrix4f implements Externalizable, Cloneable, Matrix4fc { + + private static final long serialVersionUID = 1L; + + float m00, m01, m02, m03; + float m10, m11, m12, m13; + float m20, m21, m22, m23; + float m30, m31, m32, m33; + + int properties; + + /** + * Create a new {@link Matrix4f} and set it to {@link #identity() identity}. + */ + public Matrix4f() { + this._m00(1.0f) + ._m11(1.0f) + ._m22(1.0f) + ._m33(1.0f) + ._properties(PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + } + + /** + * Create a new {@link Matrix4f} by setting its uppper left 3x3 submatrix to the values of the given {@link Matrix3fc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix3fc} + */ + public Matrix4f(Matrix3fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4f} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix4fc} to copy the values from + */ + public Matrix4f(Matrix4fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4f} and set its upper 4x3 submatrix to the given matrix mat + * and all other elements to identity. + * + * @param mat + * the {@link Matrix4x3fc} to copy the values from + */ + public Matrix4f(Matrix4x3fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4f} and make it a copy of the given matrix. + *

+ * Note that due to the given {@link Matrix4dc} storing values in double-precision and the constructed {@link Matrix4f} storing them + * in single-precision, there is the possibility of losing precision. + * + * @param mat + * the {@link Matrix4dc} to copy the values from + */ + public Matrix4f(Matrix4dc mat) { + set(mat); + } + + /** + * Create a new 4x4 matrix using the supplied float values. + *

+ * The matrix layout will be:

+ * m00, m10, m20, m30
+ * m01, m11, m21, m31
+ * m02, m12, m22, m32
+ * m03, m13, m23, m33 + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m02 + * the value of m02 + * @param m03 + * the value of m03 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + * @param m12 + * the value of m12 + * @param m13 + * the value of m13 + * @param m20 + * the value of m20 + * @param m21 + * the value of m21 + * @param m22 + * the value of m22 + * @param m23 + * the value of m23 + * @param m30 + * the value of m30 + * @param m31 + * the value of m31 + * @param m32 + * the value of m32 + * @param m33 + * the value of m33 + */ + public Matrix4f(float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) { + this._m00(m00) + ._m01(m01) + ._m02(m02) + ._m03(m03) + ._m10(m10) + ._m11(m11) + ._m12(m12) + ._m13(m13) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + .determineProperties(); + } + + /** + * Create a new {@link Matrix4f} by reading its 16 float components from the given {@link FloatBuffer} + * at the buffer's current position. + *

+ * That FloatBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link FloatBuffer} to read the matrix values from + */ + public Matrix4f(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + determineProperties(); + } + + /** + * Create a new {@link Matrix4f} and initialize its four columns using the supplied vectors. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @param col3 + * the fourth column + */ + public Matrix4f(Vector4fc col0, Vector4fc col1, Vector4fc col2, Vector4fc col3) { + set(col0, col1, col2, col3); + } + + Matrix4f _properties(int properties) { + this.properties = properties; + return this; + } + + /** + * Assume the given properties about this matrix. + *

+ * Use one or multiple of 0, {@link Matrix4fc#PROPERTY_IDENTITY}, + * {@link Matrix4fc#PROPERTY_TRANSLATION}, {@link Matrix4fc#PROPERTY_AFFINE}, + * {@link Matrix4fc#PROPERTY_PERSPECTIVE}, {@link Matrix4fc#PROPERTY_ORTHONORMAL}. + * + * @param properties + * bitset of the properties to assume about this matrix + * @return this + */ + public Matrix4f assume(int properties) { + this._properties(properties); + return this; + } + + /** + * Compute and set the matrix properties returned by {@link #properties()} based + * on the current matrix element values. + * + * @return this + */ + public Matrix4f determineProperties() { + int properties = 0; + if (m03 == 0.0f && m13 == 0.0f) { + if (m23 == 0.0f && m33 == 1.0f) { + properties |= PROPERTY_AFFINE; + if (m00 == 1.0f && m01 == 0.0f && m02 == 0.0f && m10 == 0.0f && m11 == 1.0f && m12 == 0.0f + && m20 == 0.0f && m21 == 0.0f && m22 == 1.0f) { + properties |= PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + if (m30 == 0.0f && m31 == 0.0f && m32 == 0.0f) + properties |= PROPERTY_IDENTITY; + } + /* + * We do not determine orthogonality, since it would require arbitrary epsilons + * and is rather expensive (6 dot products) in the worst case. + */ + } else if (m01 == 0.0f && m02 == 0.0f && m10 == 0.0f && m12 == 0.0f && m20 == 0.0f && m21 == 0.0f + && m30 == 0.0f && m31 == 0.0f && m33 == 0.0f) { + properties |= PROPERTY_PERSPECTIVE; + } + } + this.properties = properties; + return this; + } + + public int properties() { + return properties; + } + + public float m00() { + return m00; + } + public float m01() { + return m01; + } + public float m02() { + return m02; + } + public float m03() { + return m03; + } + public float m10() { + return m10; + } + public float m11() { + return m11; + } + public float m12() { + return m12; + } + public float m13() { + return m13; + } + public float m20() { + return m20; + } + public float m21() { + return m21; + } + public float m22() { + return m22; + } + public float m23() { + return m23; + } + public float m30() { + return m30; + } + public float m31() { + return m31; + } + public float m32() { + return m32; + } + public float m33() { + return m33; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + public Matrix4f m00(float m00) { + this.m00 = m00; + properties &= ~PROPERTY_ORTHONORMAL; + if (m00 != 1.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + public Matrix4f m01(float m01) { + this.m01 = m01; + properties &= ~PROPERTY_ORTHONORMAL; + if (m01 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2. + * + * @param m02 + * the new value + * @return this + */ + public Matrix4f m02(float m02) { + this.m02 = m02; + properties &= ~PROPERTY_ORTHONORMAL; + if (m02 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 3. + * + * @param m03 + * the new value + * @return this + */ + public Matrix4f m03(float m03) { + this.m03 = m03; + if (m03 != 0.0f) + properties = 0; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + public Matrix4f m10(float m10) { + this.m10 = m10; + properties &= ~PROPERTY_ORTHONORMAL; + if (m10 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + public Matrix4f m11(float m11) { + this.m11 = m11; + properties &= ~PROPERTY_ORTHONORMAL; + if (m11 != 1.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2. + * + * @param m12 + * the new value + * @return this + */ + public Matrix4f m12(float m12) { + this.m12 = m12; + properties &= ~PROPERTY_ORTHONORMAL; + if (m12 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 3. + * + * @param m13 + * the new value + * @return this + */ + public Matrix4f m13(float m13) { + this.m13 = m13; + if (m13 != 0.0f) + properties = 0; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + public Matrix4f m20(float m20) { + this.m20 = m20; + properties &= ~PROPERTY_ORTHONORMAL; + if (m20 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + public Matrix4f m21(float m21) { + this.m21 = m21; + properties &= ~PROPERTY_ORTHONORMAL; + if (m21 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2. + * + * @param m22 + * the new value + * @return this + */ + public Matrix4f m22(float m22) { + this.m22 = m22; + properties &= ~PROPERTY_ORTHONORMAL; + if (m22 != 1.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 3. + * + * @param m23 + * the new value + * @return this + */ + public Matrix4f m23(float m23) { + this.m23 = m23; + if (m23 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 0. + * + * @param m30 + * the new value + * @return this + */ + public Matrix4f m30(float m30) { + this.m30 = m30; + if (m30 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 1. + * + * @param m31 + * the new value + * @return this + */ + public Matrix4f m31(float m31) { + this.m31 = m31; + if (m31 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 2. + * + * @param m32 + * the new value + * @return this + */ + public Matrix4f m32(float m32) { + this.m32 = m32; + if (m32 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 3. + * + * @param m33 + * the new value + * @return this + */ + public Matrix4f m33(float m33) { + this.m33 = m33; + if (m33 != 0.0f) + properties &= ~(PROPERTY_PERSPECTIVE); + if (m33 != 1.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL | PROPERTY_AFFINE); + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0 without updating the properties of the matrix. + * + * @param m00 + * the new value + * @return this + */ + Matrix4f _m00(float m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1 without updating the properties of the matrix. + * + * @param m01 + * the new value + * @return this + */ + Matrix4f _m01(float m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2 without updating the properties of the matrix. + * + * @param m02 + * the new value + * @return this + */ + Matrix4f _m02(float m02) { + this.m02 = m02; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 3 without updating the properties of the matrix. + * + * @param m03 + * the new value + * @return this + */ + Matrix4f _m03(float m03) { + this.m03 = m03; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0 without updating the properties of the matrix. + * + * @param m10 + * the new value + * @return this + */ + Matrix4f _m10(float m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1 without updating the properties of the matrix. + * + * @param m11 + * the new value + * @return this + */ + Matrix4f _m11(float m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2 without updating the properties of the matrix. + * + * @param m12 + * the new value + * @return this + */ + Matrix4f _m12(float m12) { + this.m12 = m12; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 3 without updating the properties of the matrix. + * + * @param m13 + * the new value + * @return this + */ + Matrix4f _m13(float m13) { + this.m13 = m13; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0 without updating the properties of the matrix. + * + * @param m20 + * the new value + * @return this + */ + Matrix4f _m20(float m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1 without updating the properties of the matrix. + * + * @param m21 + * the new value + * @return this + */ + Matrix4f _m21(float m21) { + this.m21 = m21; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2 without updating the properties of the matrix. + * + * @param m22 + * the new value + * @return this + */ + Matrix4f _m22(float m22) { + this.m22 = m22; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 3 without updating the properties of the matrix. + * + * @param m23 + * the new value + * @return this + */ + Matrix4f _m23(float m23) { + this.m23 = m23; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 0 without updating the properties of the matrix. + * + * @param m30 + * the new value + * @return this + */ + Matrix4f _m30(float m30) { + this.m30 = m30; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 1 without updating the properties of the matrix. + * + * @param m31 + * the new value + * @return this + */ + Matrix4f _m31(float m31) { + this.m31 = m31; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 2 without updating the properties of the matrix. + * + * @param m32 + * the new value + * @return this + */ + Matrix4f _m32(float m32) { + this.m32 = m32; + return this; + } + + /** + * Set the value of the matrix element at column 3 and row 3 without updating the properties of the matrix. + * + * @param m33 + * the new value + * @return this + */ + Matrix4f _m33(float m33) { + this.m33 = m33; + return this; + } + + /** + * Reset this matrix to the identity. + *

+ * Please note that if a call to {@link #identity()} is immediately followed by a call to: + * {@link #translate(float, float, float) translate}, + * {@link #rotate(float, float, float, float) rotate}, + * {@link #scale(float, float, float) scale}, + * {@link #perspective(float, float, float, float) perspective}, + * {@link #frustum(float, float, float, float, float, float) frustum}, + * {@link #ortho(float, float, float, float, float, float) ortho}, + * {@link #ortho2D(float, float, float, float) ortho2D}, + * {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt}, + * {@link #lookAlong(float, float, float, float, float, float) lookAlong}, + * or any of their overloads, then the call to {@link #identity()} can be omitted and the subsequent call replaced with: + * {@link #translation(float, float, float) translation}, + * {@link #rotation(float, float, float, float) rotation}, + * {@link #scaling(float, float, float) scaling}, + * {@link #setPerspective(float, float, float, float) setPerspective}, + * {@link #setFrustum(float, float, float, float, float, float) setFrustum}, + * {@link #setOrtho(float, float, float, float, float, float) setOrtho}, + * {@link #setOrtho2D(float, float, float, float) setOrtho2D}, + * {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt}, + * {@link #setLookAlong(float, float, float, float, float, float) setLookAlong}, + * or any of their overloads. + * + * @return this + */ + public Matrix4f identity() { + if ((properties & PROPERTY_IDENTITY) != 0) + return this; + return + _m00(1.0f). + _m01(0.0f). + _m02(0.0f). + _m03(0.0f). + _m10(0.0f). + _m11(1.0f). + _m12(0.0f). + _m13(0.0f). + _m20(0.0f). + _m21(0.0f). + _m22(1.0f). + _m23(0.0f). + _m30(0.0f). + _m31(0.0f). + _m32(0.0f). + _m33(1.0f). + _properties(PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + } + + /** + * Store the values of the given matrix m into this matrix. + * + * @see #Matrix4f(Matrix4fc) + * @see #get(Matrix4f) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4f set(Matrix4fc m) { + return + _m00(m.m00()). + _m01(m.m01()). + _m02(m.m02()). + _m03(m.m03()). + _m10(m.m10()). + _m11(m.m11()). + _m12(m.m12()). + _m13(m.m13()). + _m20(m.m20()). + _m21(m.m21()). + _m22(m.m22()). + _m23(m.m23()). + _m30(m.m30()). + _m31(m.m31()). + _m32(m.m32()). + _m33(m.m33()). + _properties(m.properties()); + } + + /** + * Store the values of the transpose of the given matrix m into this matrix. + * + * @param m + * the matrix to copy the transposed values from + * @return this + */ + public Matrix4f setTransposed(Matrix4fc m) { + if ((m.properties() & PROPERTY_IDENTITY) != 0) + return this.identity(); + return setTransposedInternal(m); + } + private Matrix4f setTransposedInternal(Matrix4fc m) { + float nm10 = m.m01(), nm12 = m.m21(), nm13 = m.m31(); + float nm20 = m.m02(), nm21 = m.m12(), nm30 = m.m03(); + float nm31 = m.m13(), nm32 = m.m23(); + return this + ._m00(m.m00())._m01(m.m10())._m02(m.m20())._m03(m.m30()) + ._m10(nm10)._m11(m.m11())._m12(nm12)._m13(nm13) + ._m20(nm20)._m21(nm21)._m22(m.m22())._m23(m.m32()) + ._m30(nm30)._m31(nm31)._m32(nm32)._m33(m.m33()) + ._properties(m.properties() & PROPERTY_IDENTITY); + } + + /** + * Store the values of the given matrix m into this matrix + * and set the other matrix elements to identity. + * + * @see #Matrix4f(Matrix4x3fc) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4f set(Matrix4x3fc m) { + return + _m00(m.m00()). + _m01(m.m01()). + _m02(m.m02()). + _m03(0.0f). + _m10(m.m10()). + _m11(m.m11()). + _m12(m.m12()). + _m13(0.0f). + _m20(m.m20()). + _m21(m.m21()). + _m22(m.m22()). + _m23(0.0f). + _m30(m.m30()). + _m31(m.m31()). + _m32(m.m32()). + _m33(1.0f). + _properties(m.properties() | PROPERTY_AFFINE); + } + + /** + * Store the values of the given matrix m into this matrix. + *

+ * Note that due to the given matrix m storing values in double-precision and this matrix storing + * them in single-precision, there is the possibility to lose precision. + * + * @see #Matrix4f(Matrix4dc) + * @see #get(Matrix4d) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4f set(Matrix4dc m) { + return this + ._m00((float) m.m00()) + ._m01((float) m.m01()) + ._m02((float) m.m02()) + ._m03((float) m.m03()) + ._m10((float) m.m10()) + ._m11((float) m.m11()) + ._m12((float) m.m12()) + ._m13((float) m.m13()) + ._m20((float) m.m20()) + ._m21((float) m.m21()) + ._m22((float) m.m22()) + ._m23((float) m.m23()) + ._m30((float) m.m30()) + ._m31((float) m.m31()) + ._m32((float) m.m32()) + ._m33((float) m.m33()) + ._properties(m.properties()); + } + + /** + * Set the upper left 3x3 submatrix of this {@link Matrix4f} to the given {@link Matrix3fc} + * and the rest to identity. + * + * @see #Matrix4f(Matrix3fc) + * + * @param mat + * the {@link Matrix3fc} + * @return this + */ + public Matrix4f set(Matrix3fc mat) { + return this + ._m00(mat.m00()) + ._m01(mat.m01()) + ._m02(mat.m02()) + ._m03(0.0f) + ._m10(mat.m10()) + ._m11(mat.m11()) + ._m12(mat.m12()) + ._m13(0.0f) + ._m20(mat.m20()) + ._m21(mat.m21()) + ._m22(mat.m22()) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f). + _properties(PROPERTY_AFFINE); + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + * @return this + */ + public Matrix4f set(AxisAngle4f axisAngle) { + float x = axisAngle.x; + float y = axisAngle.y; + float z = axisAngle.z; + float angle = axisAngle.angle; + double n = Math.sqrt(x*x + y*y + z*z); + n = 1/n; + x *= n; + y *= n; + z *= n; + float s = Math.sin(angle); + float c = Math.cosFromSin(s, angle); + float omc = 1.0f - c; + this._m00((float)(c + x*x*omc)) + ._m11((float)(c + y*y*omc)) + ._m22((float)(c + z*z*omc)); + float tmp1 = x*y*omc; + float tmp2 = z*s; + this._m10((float)(tmp1 - tmp2)) + ._m01((float)(tmp1 + tmp2)); + tmp1 = x*z*omc; + tmp2 = y*s; + this._m20((float)(tmp1 + tmp2)) + ._m02((float)(tmp1 - tmp2)); + tmp1 = y*z*omc; + tmp2 = x*s; + return this + ._m21((float)(tmp1 - tmp2)) + ._m12((float)(tmp1 + tmp2)) + ._m03(0.0f) + ._m13(0.0f) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + * @return this + */ + public Matrix4f set(AxisAngle4d axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double n = Math.sqrt(x*x + y*y + z*z); + n = 1/n; + x *= n; + y *= n; + z *= n; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0 - c; + this._m00((float)(c + x*x*omc)) + ._m11((float)(c + y*y*omc)) + ._m22((float)(c + z*z*omc)); + double tmp1 = x*y*omc; + double tmp2 = z*s; + this._m10((float)(tmp1 - tmp2)) + ._m01((float)(tmp1 + tmp2)); + tmp1 = x*z*omc; + tmp2 = y*s; + this._m20((float)(tmp1 + tmp2)) + ._m02((float)(tmp1 - tmp2)); + tmp1 = y*z*omc; + tmp2 = x*s; + return this + ._m21((float)(tmp1 - tmp2)) + ._m12((float)(tmp1 + tmp2)) + ._m03(0.0f) + ._m13(0.0f) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link Quaternionfc}. + *

+ * This method is equivalent to calling: rotation(q) + *

+ * Reference: http://www.euclideanspace.com/ + * + * @see #rotation(Quaternionfc) + * + * @param q + * the {@link Quaternionfc} + * @return this + */ + public Matrix4f set(Quaternionfc q) { + return rotation(q); + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link Quaterniondc}. + *

+ * Reference: http://www.euclideanspace.com/ + * + * @param q + * the {@link Quaterniondc} + * @return this + */ + public Matrix4f set(Quaterniondc q) { + double w2 = q.w() * q.w(); + double x2 = q.x() * q.x(); + double y2 = q.y() * q.y(); + double z2 = q.z() * q.z(); + double zw = q.z() * q.w(); + double xy = q.x() * q.y(); + double xz = q.x() * q.z(); + double yw = q.y() * q.w(); + double yz = q.y() * q.z(); + double xw = q.x() * q.w(); + return + _m00((float) (w2 + x2 - z2 - y2)). + _m01((float) (xy + zw + zw + xy)). + _m02((float) (xz - yw + xz - yw)). + _m03(0.0f). + _m10((float) (-zw + xy - zw + xy)). + _m11((float) (y2 - z2 + w2 - x2)). + _m12((float) (yz + yz + xw + xw)). + _m13(0.0f). + _m20((float) (yw + xz + xz + yw)). + _m21((float) (yz + yz - xw - xw)). + _m22((float) (z2 - y2 - x2 + w2)). + _m30(0.0f). + _m31(0.0f). + _m32(0.0f). + _m33(1.0f). + _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set the upper left 3x3 submatrix of this {@link Matrix4f} to that of the given {@link Matrix4f} + * and don't change the other elements. + * + * @param mat + * the {@link Matrix4f} + * @return this + */ + public Matrix4f set3x3(Matrix4f mat) { + MemUtil.INSTANCE.copy3x3(mat, this); + return _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE)); + } + + + /** + * Set the upper 4x3 submatrix of this {@link Matrix4f} to the given {@link Matrix4x3fc} + * and don't change the other elements. + * + * @see Matrix4x3f#get(Matrix4f) + * + * @param mat + * the {@link Matrix4x3fc} + * @return this + */ + public Matrix4f set4x3(Matrix4x3fc mat) { + return + _m00(mat.m00()). + _m01(mat.m01()). + _m02(mat.m02()). + _m10(mat.m10()). + _m11(mat.m11()). + _m12(mat.m12()). + _m20(mat.m20()). + _m21(mat.m21()). + _m22(mat.m22()). + _m30(mat.m30()). + _m31(mat.m31()). + _m32(mat.m32()). + _properties(properties & mat.properties() & ~(PROPERTY_PERSPECTIVE)); + } + + /** + * Set the upper 4x3 submatrix of this {@link Matrix4f} to the upper 4x3 submatrix of the given {@link Matrix4f} + * and don't change the other elements. + * + * @param mat + * the {@link Matrix4f} + * @return this + */ + public Matrix4f set4x3(Matrix4f mat) { + MemUtil.INSTANCE.copy4x3(mat, this); + return _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE)); + } + + /** + * Multiply this matrix by the supplied right matrix and store the result in this. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4f mul(Matrix4fc right) { + return mul(right, this); + } + + public Matrix4f mul(Matrix4fc right, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_TRANSLATION) != 0 && (right.properties() & PROPERTY_AFFINE) != 0) + return mulTranslationAffine(right, dest); + else if ((properties & PROPERTY_AFFINE) != 0 && (right.properties() & PROPERTY_AFFINE) != 0) + return mulAffine(right, dest); + else if ((properties & PROPERTY_PERSPECTIVE) != 0 && (right.properties() & PROPERTY_AFFINE) != 0) + return mulPerspectiveAffine(right, dest); + else if ((right.properties() & PROPERTY_AFFINE) != 0) + return mulAffineR(right, dest); + return mul0(right, dest); + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + *

+ * This method neither assumes nor checks for any matrix properties of this or right + * and will always perform a complete 4x4 matrix multiplication. This method should only be used whenever the + * multiplied matrices do not have any properties for which there are optimized multiplication methods available. + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4f mul0(Matrix4fc right) { + return mul0(right, this); + } + + public Matrix4f mul0(Matrix4fc right, Matrix4f dest) { + float nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), Math.fma(m20, right.m02(), m30 * right.m03()))); + float nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), Math.fma(m21, right.m02(), m31 * right.m03()))); + float nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), Math.fma(m22, right.m02(), m32 * right.m03()))); + float nm03 = Math.fma(m03, right.m00(), Math.fma(m13, right.m01(), Math.fma(m23, right.m02(), m33 * right.m03()))); + float nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), Math.fma(m20, right.m12(), m30 * right.m13()))); + float nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), Math.fma(m21, right.m12(), m31 * right.m13()))); + float nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), Math.fma(m22, right.m12(), m32 * right.m13()))); + float nm13 = Math.fma(m03, right.m10(), Math.fma(m13, right.m11(), Math.fma(m23, right.m12(), m33 * right.m13()))); + float nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), Math.fma(m20, right.m22(), m30 * right.m23()))); + float nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), Math.fma(m21, right.m22(), m31 * right.m23()))); + float nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), Math.fma(m22, right.m22(), m32 * right.m23()))); + float nm23 = Math.fma(m03, right.m20(), Math.fma(m13, right.m21(), Math.fma(m23, right.m22(), m33 * right.m23()))); + float nm30 = Math.fma(m00, right.m30(), Math.fma(m10, right.m31(), Math.fma(m20, right.m32(), m30 * right.m33()))); + float nm31 = Math.fma(m01, right.m30(), Math.fma(m11, right.m31(), Math.fma(m21, right.m32(), m31 * right.m33()))); + float nm32 = Math.fma(m02, right.m30(), Math.fma(m12, right.m31(), Math.fma(m22, right.m32(), m32 * right.m33()))); + float nm33 = Math.fma(m03, right.m30(), Math.fma(m13, right.m31(), Math.fma(m23, right.m32(), m33 * right.m33()))); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Multiply this matrix by the matrix with the supplied elements. + *

+ * If M is this matrix and R the right matrix whose + * elements are supplied via the parameters, then the new matrix will be M * R. + * So when transforming a vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param r00 + * the m00 element of the right matrix + * @param r01 + * the m01 element of the right matrix + * @param r02 + * the m02 element of the right matrix + * @param r03 + * the m03 element of the right matrix + * @param r10 + * the m10 element of the right matrix + * @param r11 + * the m11 element of the right matrix + * @param r12 + * the m12 element of the right matrix + * @param r13 + * the m13 element of the right matrix + * @param r20 + * the m20 element of the right matrix + * @param r21 + * the m21 element of the right matrix + * @param r22 + * the m22 element of the right matrix + * @param r23 + * the m23 element of the right matrix + * @param r30 + * the m30 element of the right matrix + * @param r31 + * the m31 element of the right matrix + * @param r32 + * the m32 element of the right matrix + * @param r33 + * the m33 element of the right matrix + * @return this + */ + public Matrix4f mul( + float r00, float r01, float r02, float r03, + float r10, float r11, float r12, float r13, + float r20, float r21, float r22, float r23, + float r30, float r31, float r32, float r33) { + return mul(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, this); + } + + public Matrix4f mul( + float r00, float r01, float r02, float r03, + float r10, float r11, float r12, float r13, + float r20, float r21, float r22, float r23, + float r30, float r31, float r32, float r33, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33); + else if ((properties & PROPERTY_AFFINE) != 0) + return mulAffineL(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest); + return mulGeneric(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest); + } + private Matrix4f mulAffineL( + float r00, float r01, float r02, float r03, + float r10, float r11, float r12, float r13, + float r20, float r21, float r22, float r23, + float r30, float r31, float r32, float r33, Matrix4f dest) { + float nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03))); + float nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03))); + float nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03))); + float nm03 = r03; + float nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13))); + float nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13))); + float nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13))); + float nm13 = r13; + float nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23))); + float nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23))); + float nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23))); + float nm23 = r23; + float nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33))); + float nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33))); + float nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33))); + float nm33 = r33; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(PROPERTY_AFFINE); + } + private Matrix4f mulGeneric( + float r00, float r01, float r02, float r03, + float r10, float r11, float r12, float r13, + float r20, float r21, float r22, float r23, + float r30, float r31, float r32, float r33, Matrix4f dest) { + float nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03))); + float nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03))); + float nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03))); + float nm03 = Math.fma(m03, r00, Math.fma(m13, r01, Math.fma(m23, r02, m33 * r03))); + float nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13))); + float nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13))); + float nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13))); + float nm13 = Math.fma(m03, r10, Math.fma(m13, r11, Math.fma(m23, r12, m33 * r13))); + float nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23))); + float nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23))); + float nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23))); + float nm23 = Math.fma(m03, r20, Math.fma(m13, r21, Math.fma(m23, r22, m33 * r23))); + float nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33))); + float nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33))); + float nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33))); + float nm33 = Math.fma(m03, r30, Math.fma(m13, r31, Math.fma(m23, r32, m33 * r33))); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Multiply this matrix by the 3x3 matrix with the supplied elements expanded to a 4x4 matrix with + * all other matrix elements set to identity. + *

+ * If M is this matrix and R the right matrix whose + * elements are supplied via the parameters, then the new matrix will be M * R. + * So when transforming a vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param r00 + * the m00 element of the right matrix + * @param r01 + * the m01 element of the right matrix + * @param r02 + * the m02 element of the right matrix + * @param r10 + * the m10 element of the right matrix + * @param r11 + * the m11 element of the right matrix + * @param r12 + * the m12 element of the right matrix + * @param r20 + * the m20 element of the right matrix + * @param r21 + * the m21 element of the right matrix + * @param r22 + * the m22 element of the right matrix + * @return this + */ + public Matrix4f mul3x3( + float r00, float r01, float r02, + float r10, float r11, float r12, + float r20, float r21, float r22) { + return mul3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, this); + } + public Matrix4f mul3x3( + float r00, float r01, float r02, + float r10, float r11, float r12, + float r20, float r21, float r22, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(r00, r01, r02, 0, r10, r11, r12, 0, r20, r21, r22, 0, 0, 0, 0, 1); + return mulGeneric3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, dest); + } + private Matrix4f mulGeneric3x3( + float r00, float r01, float r02, + float r10, float r11, float r12, + float r20, float r21, float r22, Matrix4f dest) { + float nm00 = Math.fma(m00, r00, Math.fma(m10, r01, m20 * r02)); + float nm01 = Math.fma(m01, r00, Math.fma(m11, r01, m21 * r02)); + float nm02 = Math.fma(m02, r00, Math.fma(m12, r01, m22 * r02)); + float nm03 = Math.fma(m03, r00, Math.fma(m13, r01, m23 * r02)); + float nm10 = Math.fma(m00, r10, Math.fma(m10, r11, m20 * r12)); + float nm11 = Math.fma(m01, r10, Math.fma(m11, r11, m21 * r12)); + float nm12 = Math.fma(m02, r10, Math.fma(m12, r11, m22 * r12)); + float nm13 = Math.fma(m03, r10, Math.fma(m13, r11, m23 * r12)); + float nm20 = Math.fma(m00, r20, Math.fma(m10, r21, m20 * r22)); + float nm21 = Math.fma(m01, r20, Math.fma(m11, r21, m21 * r22)); + float nm22 = Math.fma(m02, r20, Math.fma(m12, r21, m22 * r22)); + float nm23 = Math.fma(m03, r20, Math.fma(m13, r21, m23 * r22)); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(this.properties & PROPERTY_AFFINE); + } + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in this. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @return this + */ + public Matrix4f mulLocal(Matrix4fc left) { + return mulLocal(left, this); + } + + public Matrix4f mulLocal(Matrix4fc left, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(left); + else if ((left.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_AFFINE) != 0 && (left.properties() & PROPERTY_AFFINE) != 0) + return mulLocalAffine(left, dest); + return mulLocalGeneric(left, dest); + } + private Matrix4f mulLocalGeneric(Matrix4fc left, Matrix4f dest) { + float nm00 = Math.fma(left.m00(), m00, Math.fma(left.m10(), m01, Math.fma(left.m20(), m02, left.m30() * m03))); + float nm01 = Math.fma(left.m01(), m00, Math.fma(left.m11(), m01, Math.fma(left.m21(), m02, left.m31() * m03))); + float nm02 = Math.fma(left.m02(), m00, Math.fma(left.m12(), m01, Math.fma(left.m22(), m02, left.m32() * m03))); + float nm03 = Math.fma(left.m03(), m00, Math.fma(left.m13(), m01, Math.fma(left.m23(), m02, left.m33() * m03))); + float nm10 = Math.fma(left.m00(), m10, Math.fma(left.m10(), m11, Math.fma(left.m20(), m12, left.m30() * m13))); + float nm11 = Math.fma(left.m01(), m10, Math.fma(left.m11(), m11, Math.fma(left.m21(), m12, left.m31() * m13))); + float nm12 = Math.fma(left.m02(), m10, Math.fma(left.m12(), m11, Math.fma(left.m22(), m12, left.m32() * m13))); + float nm13 = Math.fma(left.m03(), m10, Math.fma(left.m13(), m11, Math.fma(left.m23(), m12, left.m33() * m13))); + float nm20 = Math.fma(left.m00(), m20, Math.fma(left.m10(), m21, Math.fma(left.m20(), m22, left.m30() * m23))); + float nm21 = Math.fma(left.m01(), m20, Math.fma(left.m11(), m21, Math.fma(left.m21(), m22, left.m31() * m23))); + float nm22 = Math.fma(left.m02(), m20, Math.fma(left.m12(), m21, Math.fma(left.m22(), m22, left.m32() * m23))); + float nm23 = Math.fma(left.m03(), m20, Math.fma(left.m13(), m21, Math.fma(left.m23(), m22, left.m33() * m23))); + float nm30 = Math.fma(left.m00(), m30, Math.fma(left.m10(), m31, Math.fma(left.m20(), m32, left.m30() * m33))); + float nm31 = Math.fma(left.m01(), m30, Math.fma(left.m11(), m31, Math.fma(left.m21(), m32, left.m31() * m33))); + float nm32 = Math.fma(left.m02(), m30, Math.fma(left.m12(), m31, Math.fma(left.m22(), m32, left.m32() * m33))); + float nm33 = Math.fma(left.m03(), m30, Math.fma(left.m13(), m31, Math.fma(left.m23(), m32, left.m33() * m33))); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Pre-multiply this matrix by the supplied left matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in this. + *

+ * This method assumes that this matrix and the given left matrix both represent an {@link #isAffine() affine} transformation + * (i.e. their last rows are equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * This method will not modify either the last row of this or the last row of left. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @return this + */ + public Matrix4f mulLocalAffine(Matrix4fc left) { + return mulLocalAffine(left, this); + } + + public Matrix4f mulLocalAffine(Matrix4fc left, Matrix4f dest) { + float nm00 = left.m00() * m00 + left.m10() * m01 + left.m20() * m02; + float nm01 = left.m01() * m00 + left.m11() * m01 + left.m21() * m02; + float nm02 = left.m02() * m00 + left.m12() * m01 + left.m22() * m02; + float nm03 = left.m03(); + float nm10 = left.m00() * m10 + left.m10() * m11 + left.m20() * m12; + float nm11 = left.m01() * m10 + left.m11() * m11 + left.m21() * m12; + float nm12 = left.m02() * m10 + left.m12() * m11 + left.m22() * m12; + float nm13 = left.m13(); + float nm20 = left.m00() * m20 + left.m10() * m21 + left.m20() * m22; + float nm21 = left.m01() * m20 + left.m11() * m21 + left.m21() * m22; + float nm22 = left.m02() * m20 + left.m12() * m21 + left.m22() * m22; + float nm23 = left.m23(); + float nm30 = left.m00() * m30 + left.m10() * m31 + left.m20() * m32 + left.m30(); + float nm31 = left.m01() * m30 + left.m11() * m31 + left.m21() * m32 + left.m31(); + float nm32 = left.m02() * m30 + left.m12() * m31 + left.m22() * m32 + left.m32(); + float nm33 = left.m33(); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(PROPERTY_AFFINE | (this.properties() & left.properties() & PROPERTY_ORTHONORMAL)); + } + + /** + * Multiply this matrix by the supplied right matrix and store the result in this. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4f mul(Matrix4x3fc right) { + return mul(right, this); + } + + public Matrix4f mul(Matrix4x3fc right, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return mulTranslation(right, dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return mulAffine(right, dest); + else if ((properties & PROPERTY_PERSPECTIVE) != 0) + return mulPerspectiveAffine(right, dest); + return mulGeneric(right, dest); + } + private Matrix4f mulTranslation(Matrix4x3fc right, Matrix4f dest) { + return dest + ._m00(right.m00()) + ._m01(right.m01()) + ._m02(right.m02()) + ._m03(m03) + ._m10(right.m10()) + ._m11(right.m11()) + ._m12(right.m12()) + ._m13(m13) + ._m20(right.m20()) + ._m21(right.m21()) + ._m22(right.m22()) + ._m23(m23) + ._m30(right.m30() + m30) + ._m31(right.m31() + m31) + ._m32(right.m32() + m32) + ._m33(m33) + ._properties(PROPERTY_AFFINE | (right.properties() & PROPERTY_ORTHONORMAL)); + } + private Matrix4f mulAffine(Matrix4x3fc right, Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + float m20 = this.m20, m21 = this.m21, m22 = this.m22; + float rm00 = right.m00(), rm01 = right.m01(), rm02 = right.m02(); + float rm10 = right.m10(), rm11 = right.m11(), rm12 = right.m12(); + float rm20 = right.m20(), rm21 = right.m21(), rm22 = right.m22(); + float rm30 = right.m30(), rm31 = right.m31(), rm32 = right.m32(); + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m03(m03) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m13(m13) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m23(m23) + ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30)))) + ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31)))) + ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32)))) + ._m33(m33) + ._properties(PROPERTY_AFFINE | (this.properties & right.properties() & PROPERTY_ORTHONORMAL)); + } + private Matrix4f mulGeneric(Matrix4x3fc right, Matrix4f dest) { + float nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), m20 * right.m02())); + float nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), m21 * right.m02())); + float nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), m22 * right.m02())); + float nm03 = Math.fma(m03, right.m00(), Math.fma(m13, right.m01(), m23 * right.m02())); + float nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), m20 * right.m12())); + float nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), m21 * right.m12())); + float nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), m22 * right.m12())); + float nm13 = Math.fma(m03, right.m10(), Math.fma(m13, right.m11(), m23 * right.m12())); + float nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), m20 * right.m22())); + float nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), m21 * right.m22())); + float nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), m22 * right.m22())); + float nm23 = Math.fma(m03, right.m20(), Math.fma(m13, right.m21(), m23 * right.m22())); + float nm30 = Math.fma(m00, right.m30(), Math.fma(m10, right.m31(), Math.fma(m20, right.m32(), m30))); + float nm31 = Math.fma(m01, right.m30(), Math.fma(m11, right.m31(), Math.fma(m21, right.m32(), m31))); + float nm32 = Math.fma(m02, right.m30(), Math.fma(m12, right.m31(), Math.fma(m22, right.m32(), m32))); + float nm33 = Math.fma(m03, right.m30(), Math.fma(m13, right.m31(), Math.fma(m23, right.m32(), m33))); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + } + + /** + * Multiply this matrix by the supplied right matrix and store the result in this. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4f mul(Matrix3x2fc right) { + return mul(right, this); + } + + public Matrix4f mul(Matrix3x2fc right, Matrix4f dest) { + float nm00 = m00 * right.m00() + m10 * right.m01(); + float nm01 = m01 * right.m00() + m11 * right.m01(); + float nm02 = m02 * right.m00() + m12 * right.m01(); + float nm03 = m03 * right.m00() + m13 * right.m01(); + float nm10 = m00 * right.m10() + m10 * right.m11(); + float nm11 = m01 * right.m10() + m11 * right.m11(); + float nm12 = m02 * right.m10() + m12 * right.m11(); + float nm13 = m03 * right.m10() + m13 * right.m11(); + float nm30 = m00 * right.m20() + m10 * right.m21() + m30; + float nm31 = m01 * right.m20() + m11 * right.m21() + m31; + float nm32 = m02 * right.m20() + m12 * right.m21() + m32; + float nm33 = m03 * right.m20() + m13 * right.m21() + m33; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + } + + /** + * Multiply this symmetric perspective projection matrix by the supplied {@link #isAffine() affine} view matrix. + *

+ * If P is this matrix and V the view matrix, + * then the new matrix will be P * V. So when transforming a + * vector v with the new matrix by using P * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the {@link #isAffine() affine} matrix to multiply this symmetric perspective projection matrix by + * @return this + */ + public Matrix4f mulPerspectiveAffine(Matrix4fc view) { + return mulPerspectiveAffine(view, this); + } + + public Matrix4f mulPerspectiveAffine(Matrix4fc view, Matrix4f dest) { + float nm00 = m00 * view.m00(), nm01 = m11 * view.m01(), nm02 = m22 * view.m02(), nm03 = m23 * view.m02(); + float nm10 = m00 * view.m10(), nm11 = m11 * view.m11(), nm12 = m22 * view.m12(), nm13 = m23 * view.m12(); + float nm20 = m00 * view.m20(), nm21 = m11 * view.m21(), nm22 = m22 * view.m22(), nm23 = m23 * view.m22(); + float nm30 = m00 * view.m30(), nm31 = m11 * view.m31(), nm32 = m22 * view.m32() + m32, nm33 = m23 * view.m32(); + return dest + ._m00(nm00)._m01(nm01)._m02(nm02)._m03(nm03) + ._m10(nm10)._m11(nm11)._m12(nm12)._m13(nm13) + ._m20(nm20)._m21(nm21)._m22(nm22)._m23(nm23) + ._m30(nm30)._m31(nm31)._m32(nm32)._m33(nm33) + ._properties(0); + } + + /** + * Multiply this symmetric perspective projection matrix by the supplied view matrix. + *

+ * If P is this matrix and V the view matrix, + * then the new matrix will be P * V. So when transforming a + * vector v with the new matrix by using P * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the matrix to multiply this symmetric perspective projection matrix by + * @return this + */ + public Matrix4f mulPerspectiveAffine(Matrix4x3fc view) { + return mulPerspectiveAffine(view, this); + } + + public Matrix4f mulPerspectiveAffine(Matrix4x3fc view, Matrix4f dest) { + float lm00 = m00, lm11 = m11, lm22 = m22, lm23 = m23; + return dest. + _m00(lm00 * view.m00())._m01(lm11 * view.m01())._m02(lm22 * view.m02())._m03(lm23 * view.m02()). + _m10(lm00 * view.m10())._m11(lm11 * view.m11())._m12(lm22 * view.m12())._m13(lm23 * view.m12()). + _m20(lm00 * view.m20())._m21(lm11 * view.m21())._m22(lm22 * view.m22())._m23(lm23 * view.m22()). + _m30(lm00 * view.m30())._m31(lm11 * view.m31())._m32(lm22 * view.m32() + m32)._m33(lm23 * view.m32()). + _properties(0); + } + + /** + * Multiply this matrix by the supplied right matrix, which is assumed to be {@link #isAffine() affine}, and store the result in this. + *

+ * This method assumes that the given right matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @return this + */ + public Matrix4f mulAffineR(Matrix4fc right) { + return mulAffineR(right, this); + } + + public Matrix4f mulAffineR(Matrix4fc right, Matrix4f dest) { + float nm00 = Math.fma(m00, right.m00(), Math.fma(m10, right.m01(), m20 * right.m02())); + float nm01 = Math.fma(m01, right.m00(), Math.fma(m11, right.m01(), m21 * right.m02())); + float nm02 = Math.fma(m02, right.m00(), Math.fma(m12, right.m01(), m22 * right.m02())); + float nm03 = Math.fma(m03, right.m00(), Math.fma(m13, right.m01(), m23 * right.m02())); + float nm10 = Math.fma(m00, right.m10(), Math.fma(m10, right.m11(), m20 * right.m12())); + float nm11 = Math.fma(m01, right.m10(), Math.fma(m11, right.m11(), m21 * right.m12())); + float nm12 = Math.fma(m02, right.m10(), Math.fma(m12, right.m11(), m22 * right.m12())); + float nm13 = Math.fma(m03, right.m10(), Math.fma(m13, right.m11(), m23 * right.m12())); + float nm20 = Math.fma(m00, right.m20(), Math.fma(m10, right.m21(), m20 * right.m22())); + float nm21 = Math.fma(m01, right.m20(), Math.fma(m11, right.m21(), m21 * right.m22())); + float nm22 = Math.fma(m02, right.m20(), Math.fma(m12, right.m21(), m22 * right.m22())); + float nm23 = Math.fma(m03, right.m20(), Math.fma(m13, right.m21(), m23 * right.m22())); + float nm30 = Math.fma(m00, right.m30(), Math.fma(m10, right.m31(), Math.fma(m20, right.m32(), m30))); + float nm31 = Math.fma(m01, right.m30(), Math.fma(m11, right.m31(), Math.fma(m21, right.m32(), m31))); + float nm32 = Math.fma(m02, right.m30(), Math.fma(m12, right.m31(), Math.fma(m22, right.m32(), m32))); + float nm33 = Math.fma(m03, right.m30(), Math.fma(m13, right.m31(), Math.fma(m23, right.m32(), m33))); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + } + + /** + * Multiply this matrix by the supplied right matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in this. + *

+ * This method assumes that this matrix and the given right matrix both represent an {@link #isAffine() affine} transformation + * (i.e. their last rows are equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @return this + */ + public Matrix4f mulAffine(Matrix4fc right) { + return mulAffine(right, this); + } + + public Matrix4f mulAffine(Matrix4fc right, Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + float m20 = this.m20, m21 = this.m21, m22 = this.m22; + float rm00 = right.m00(), rm01 = right.m01(), rm02 = right.m02(); + float rm10 = right.m10(), rm11 = right.m11(), rm12 = right.m12(); + float rm20 = right.m20(), rm21 = right.m21(), rm22 = right.m22(); + float rm30 = right.m30(), rm31 = right.m31(), rm32 = right.m32(); + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m03(m03) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m13(m13) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m23(m23) + ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30)))) + ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31)))) + ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32)))) + ._m33(m33) + ._properties(PROPERTY_AFFINE | (this.properties & right.properties() & PROPERTY_ORTHONORMAL)); + } + + public Matrix4f mulTranslationAffine(Matrix4fc right, Matrix4f dest) { + return dest + ._m00(right.m00()) + ._m01(right.m01()) + ._m02(right.m02()) + ._m03(m03) + ._m10(right.m10()) + ._m11(right.m11()) + ._m12(right.m12()) + ._m13(m13) + ._m20(right.m20()) + ._m21(right.m21()) + ._m22(right.m22()) + ._m23(m23) + ._m30(right.m30() + m30) + ._m31(right.m31() + m31) + ._m32(right.m32() + m32) + ._m33(m33) + ._properties(PROPERTY_AFFINE | (right.properties() & PROPERTY_ORTHONORMAL)); + } + + /** + * Multiply this orthographic projection matrix by the supplied {@link #isAffine() affine} view matrix. + *

+ * If M is this matrix and V the view matrix, + * then the new matrix will be M * V. So when transforming a + * vector v with the new matrix by using M * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the affine matrix which to multiply this with + * @return this + */ + public Matrix4f mulOrthoAffine(Matrix4fc view) { + return mulOrthoAffine(view, this); + } + + public Matrix4f mulOrthoAffine(Matrix4fc view, Matrix4f dest) { + float nm00 = m00 * view.m00(); + float nm01 = m11 * view.m01(); + float nm02 = m22 * view.m02(); + float nm10 = m00 * view.m10(); + float nm11 = m11 * view.m11(); + float nm12 = m22 * view.m12(); + float nm20 = m00 * view.m20(); + float nm21 = m11 * view.m21(); + float nm22 = m22 * view.m22(); + float nm30 = m00 * view.m30() + m30; + float nm31 = m11 * view.m31() + m31; + float nm32 = m22 * view.m32() + m32; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0f) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0f) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(0.0f) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE); + } + + /** + * Component-wise add the upper 4x3 submatrices of this and other + * by first multiplying each component of other's 4x3 submatrix by otherFactor and + * adding that result to this. + *

+ * The matrix other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's 4x3 components + * @return this + */ + public Matrix4f fma4x3(Matrix4fc other, float otherFactor) { + return fma4x3(other, otherFactor, this); + } + + public Matrix4f fma4x3(Matrix4fc other, float otherFactor, Matrix4f dest) { + dest._m00(Math.fma(other.m00(), otherFactor, m00)) + ._m01(Math.fma(other.m01(), otherFactor, m01)) + ._m02(Math.fma(other.m02(), otherFactor, m02)) + ._m03(m03) + ._m10(Math.fma(other.m10(), otherFactor, m10)) + ._m11(Math.fma(other.m11(), otherFactor, m11)) + ._m12(Math.fma(other.m12(), otherFactor, m12)) + ._m13(m13) + ._m20(Math.fma(other.m20(), otherFactor, m20)) + ._m21(Math.fma(other.m21(), otherFactor, m21)) + ._m22(Math.fma(other.m22(), otherFactor, m22)) + ._m23(m23) + ._m30(Math.fma(other.m30(), otherFactor, m30)) + ._m31(Math.fma(other.m31(), otherFactor, m31)) + ._m32(Math.fma(other.m32(), otherFactor, m32)) + ._m33(m33) + ._properties(0); + return dest; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix4f add(Matrix4fc other) { + return add(other, this); + } + + public Matrix4f add(Matrix4fc other, Matrix4f dest) { + dest._m00(m00 + other.m00()) + ._m01(m01 + other.m01()) + ._m02(m02 + other.m02()) + ._m03(m03 + other.m03()) + ._m10(m10 + other.m10()) + ._m11(m11 + other.m11()) + ._m12(m12 + other.m12()) + ._m13(m13 + other.m13()) + ._m20(m20 + other.m20()) + ._m21(m21 + other.m21()) + ._m22(m22 + other.m22()) + ._m23(m23 + other.m23()) + ._m30(m30 + other.m30()) + ._m31(m31 + other.m31()) + ._m32(m32 + other.m32()) + ._m33(m33 + other.m33()) + ._properties(0); + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix4f sub(Matrix4fc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix4f sub(Matrix4fc subtrahend, Matrix4f dest) { + dest._m00(m00 - subtrahend.m00()) + ._m01(m01 - subtrahend.m01()) + ._m02(m02 - subtrahend.m02()) + ._m03(m03 - subtrahend.m03()) + ._m10(m10 - subtrahend.m10()) + ._m11(m11 - subtrahend.m11()) + ._m12(m12 - subtrahend.m12()) + ._m13(m13 - subtrahend.m13()) + ._m20(m20 - subtrahend.m20()) + ._m21(m21 - subtrahend.m21()) + ._m22(m22 - subtrahend.m22()) + ._m23(m23 - subtrahend.m23()) + ._m30(m30 - subtrahend.m30()) + ._m31(m31 - subtrahend.m31()) + ._m32(m32 - subtrahend.m32()) + ._m33(m33 - subtrahend.m33()) + ._properties(0); + return dest; + } + + /** + * Component-wise multiply this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix4f mulComponentWise(Matrix4fc other) { + return mulComponentWise(other, this); + } + + public Matrix4f mulComponentWise(Matrix4fc other, Matrix4f dest) { + dest._m00(m00 * other.m00()) + ._m01(m01 * other.m01()) + ._m02(m02 * other.m02()) + ._m03(m03 * other.m03()) + ._m10(m10 * other.m10()) + ._m11(m11 * other.m11()) + ._m12(m12 * other.m12()) + ._m13(m13 * other.m13()) + ._m20(m20 * other.m20()) + ._m21(m21 * other.m21()) + ._m22(m22 * other.m22()) + ._m23(m23 * other.m23()) + ._m30(m30 * other.m30()) + ._m31(m31 * other.m31()) + ._m32(m32 * other.m32()) + ._m33(m33 * other.m33()) + ._properties(0); + return dest; + } + + /** + * Component-wise add the upper 4x3 submatrices of this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix4f add4x3(Matrix4fc other) { + return add4x3(other, this); + } + + public Matrix4f add4x3(Matrix4fc other, Matrix4f dest) { + dest._m00(m00 + other.m00()) + ._m01(m01 + other.m01()) + ._m02(m02 + other.m02()) + ._m03(m03) + ._m10(m10 + other.m10()) + ._m11(m11 + other.m11()) + ._m12(m12 + other.m12()) + ._m13(m13) + ._m20(m20 + other.m20()) + ._m21(m21 + other.m21()) + ._m22(m22 + other.m22()) + ._m23(m23) + ._m30(m30 + other.m30()) + ._m31(m31 + other.m31()) + ._m32(m32 + other.m32()) + ._m33(m33) + ._properties(0); + return dest; + } + + /** + * Component-wise subtract the upper 4x3 submatrices of subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix4f sub4x3(Matrix4f subtrahend) { + return sub4x3(subtrahend, this); + } + + public Matrix4f sub4x3(Matrix4fc subtrahend, Matrix4f dest) { + dest._m00(m00 - subtrahend.m00()) + ._m01(m01 - subtrahend.m01()) + ._m02(m02 - subtrahend.m02()) + ._m03(m03) + ._m10(m10 - subtrahend.m10()) + ._m11(m11 - subtrahend.m11()) + ._m12(m12 - subtrahend.m12()) + ._m13(m13) + ._m20(m20 - subtrahend.m20()) + ._m21(m21 - subtrahend.m21()) + ._m22(m22 - subtrahend.m22()) + ._m23(m23) + ._m30(m30 - subtrahend.m30()) + ._m31(m31 - subtrahend.m31()) + ._m32(m32 - subtrahend.m32()) + ._m33(m33) + ._properties(0); + return dest; + } + + /** + * Component-wise multiply the upper 4x3 submatrices of this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix4f mul4x3ComponentWise(Matrix4fc other) { + return mul4x3ComponentWise(other, this); + } + + public Matrix4f mul4x3ComponentWise(Matrix4fc other, Matrix4f dest) { + dest._m00(m00 * other.m00()) + ._m01(m01 * other.m01()) + ._m02(m02 * other.m02()) + ._m03(m03) + ._m10(m10 * other.m10()) + ._m11(m11 * other.m11()) + ._m12(m12 * other.m12()) + ._m13(m13) + ._m20(m20 * other.m20()) + ._m21(m21 * other.m21()) + ._m22(m22 * other.m22()) + ._m23(m23) + ._m30(m30 * other.m30()) + ._m31(m31 * other.m31()) + ._m32(m32 * other.m32()) + ._m33(m33) + ._properties(0); + return dest; + } + + /** + * Set the values within this matrix to the supplied float values. The matrix will look like this:

+ * + * m00, m10, m20, m30
+ * m01, m11, m21, m31
+ * m02, m12, m22, m32
+ * m03, m13, m23, m33 + * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m02 + * the new value of m02 + * @param m03 + * the new value of m03 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @param m12 + * the new value of m12 + * @param m13 + * the new value of m13 + * @param m20 + * the new value of m20 + * @param m21 + * the new value of m21 + * @param m22 + * the new value of m22 + * @param m23 + * the new value of m23 + * @param m30 + * the new value of m30 + * @param m31 + * the new value of m31 + * @param m32 + * the new value of m32 + * @param m33 + * the new value of m33 + * @return this + */ + public Matrix4f set(float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) { + return this + ._m00(m00) + ._m10(m10) + ._m20(m20) + ._m30(m30) + ._m01(m01) + ._m11(m11) + ._m21(m21) + ._m31(m31) + ._m02(m02) + ._m12(m12) + ._m22(m22) + ._m32(m32) + ._m03(m03) + ._m13(m13) + ._m23(m23) + ._m33(m33) + .determineProperties(); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 4, 8, 12
+ * 1, 5, 9, 13
+ * 2, 6, 10, 14
+ * 3, 7, 11, 15
+ * + * @see #set(float[]) + * + * @param m + * the array to read the matrix values from + * @param off + * the offset into the array + * @return this + */ + public Matrix4f set(float m[], int off) { + MemUtil.INSTANCE.copy(m, off, this); + return determineProperties(); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 4, 8, 12
+ * 1, 5, 9, 13
+ * 2, 6, 10, 14
+ * 3, 7, 11, 15
+ * + * @see #set(float[], int) + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix4f set(float m[]) { + return set(m, 0); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in row-major order. + *

+ * The results will look like this:

+ * + * 0, 1, 2, 3
+ * 4, 5, 6, 7
+ * 8, 9, 10, 11
+ * 12, 13, 14, 15
+ * + * @see #setTransposed(float[]) + * + * @param m + * the array to read the matrix values from + * @param off + * the offset into the array + * @return this + */ + public Matrix4f setTransposed(float m[], int off) { + MemUtil.INSTANCE.copyTransposed(m, off, this); + return determineProperties(); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in row-major order. + *

+ * The results will look like this:

+ * + * 0, 1, 2, 3
+ * 4, 5, 6, 7
+ * 8, 9, 10, 11
+ * 12, 13, 14, 15
+ * + * @see #setTransposed(float[], int) + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix4f setTransposed(float m[]) { + return setTransposed(m, 0); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link FloatBuffer} in column-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4f set(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4f set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link FloatBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4f set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4f set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link FloatBuffer} in row-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in row-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in row-major order + * @return this + */ + public Matrix4f setTransposed(FloatBuffer buffer) { + MemUtil.INSTANCE.getTransposed(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from the given {@link ByteBuffer} in row-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in row-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in row-major order + * @return this + */ + public Matrix4f setTransposed(ByteBuffer buffer) { + MemUtil.INSTANCE.getTransposed(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix4f setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 16 float values from off-heap memory in row-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in row-major order + * @return this + */ + public Matrix4f setTransposedFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.getTransposed(this, address); + return determineProperties(); + } + + /** + * Set the four columns of this matrix to the supplied vectors, respectively. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @param col3 + * the fourth column + * @return this + */ + public Matrix4f set(Vector4fc col0, Vector4fc col1, Vector4fc col2, Vector4fc col3) { + return + _m00(col0.x()). + _m01(col0.y()). + _m02(col0.z()). + _m03(col0.w()). + _m10(col1.x()). + _m11(col1.y()). + _m12(col1.z()). + _m13(col1.w()). + _m20(col2.x()). + _m21(col2.y()). + _m22(col2.z()). + _m23(col2.w()). + _m30(col3.x()). + _m31(col3.y()). + _m32(col3.z()). + _m33(col3.w()). + determineProperties(); + } + + public float determinant() { + if ((properties & PROPERTY_AFFINE) != 0) + return determinantAffine(); + return (m00 * m11 - m01 * m10) * (m22 * m33 - m23 * m32) + + (m02 * m10 - m00 * m12) * (m21 * m33 - m23 * m31) + + (m00 * m13 - m03 * m10) * (m21 * m32 - m22 * m31) + + (m01 * m12 - m02 * m11) * (m20 * m33 - m23 * m30) + + (m03 * m11 - m01 * m13) * (m20 * m32 - m22 * m30) + + (m02 * m13 - m03 * m12) * (m20 * m31 - m21 * m30); + } + + public float determinant3x3() { + return (m00 * m11 - m01 * m10) * m22 + + (m02 * m10 - m00 * m12) * m21 + + (m01 * m12 - m02 * m11) * m20; + } + + public float determinantAffine() { + return (m00 * m11 - m01 * m10) * m22 + + (m02 * m10 - m00 * m12) * m21 + + (m01 * m12 - m02 * m11) * m20; + } + + public Matrix4f invert(Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) { + return dest.identity(); + } else if ((properties & PROPERTY_TRANSLATION) != 0) + return invertTranslation(dest); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return invertOrthonormal(dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return invertAffine(dest); + else if ((properties & PROPERTY_PERSPECTIVE) != 0) + return invertPerspective(dest); + return invertGeneric(dest); + } + private Matrix4f invertTranslation(Matrix4f dest) { + if (dest != this) + dest.set(this); + return dest._m30(-m30)._m31(-m31)._m32(-m32)._properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + } + private Matrix4f invertOrthonormal(Matrix4f dest) { + float nm30 = -(m00 * m30 + m01 * m31 + m02 * m32); + float nm31 = -(m10 * m30 + m11 * m31 + m12 * m32); + float nm32 = -(m20 * m30 + m21 * m31 + m22 * m32); + float m01 = this.m01; + float m02 = this.m02; + float m12 = this.m12; + return dest + ._m00(m00) + ._m01(m10) + ._m02(m20) + ._m03(0.0f) + ._m10(m01) + ._m11(m11) + ._m12(m21) + ._m13(0.0f) + ._m20(m02) + ._m21(m12) + ._m22(m22) + ._m23(0.0f) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + private Matrix4f invertGeneric(Matrix4f dest) { + if (this != dest) + return invertGenericNonThis(dest); + return invertGenericThis(dest); + } + private Matrix4f invertGenericNonThis(Matrix4f dest) { + float a = m00 * m11 - m01 * m10; + float b = m00 * m12 - m02 * m10; + float c = m00 * m13 - m03 * m10; + float d = m01 * m12 - m02 * m11; + float e = m01 * m13 - m03 * m11; + float f = m02 * m13 - m03 * m12; + float g = m20 * m31 - m21 * m30; + float h = m20 * m32 - m22 * m30; + float i = m20 * m33 - m23 * m30; + float j = m21 * m32 - m22 * m31; + float k = m21 * m33 - m23 * m31; + float l = m22 * m33 - m23 * m32; + float det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0f / det; + return dest + ._m00(Math.fma( m11, l, Math.fma(-m12, k, m13 * j)) * det) + ._m01(Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det) + ._m02(Math.fma( m31, f, Math.fma(-m32, e, m33 * d)) * det) + ._m03(Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det) + ._m10(Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det) + ._m11(Math.fma( m00, l, Math.fma(-m02, i, m03 * h)) * det) + ._m12(Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det) + ._m13(Math.fma( m20, f, Math.fma(-m22, c, m23 * b)) * det) + ._m20(Math.fma( m10, k, Math.fma(-m11, i, m13 * g)) * det) + ._m21(Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det) + ._m22(Math.fma( m30, e, Math.fma(-m31, c, m33 * a)) * det) + ._m23(Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det) + ._m30(Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det) + ._m31(Math.fma( m00, j, Math.fma(-m01, h, m02 * g)) * det) + ._m32(Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det) + ._m33(Math.fma( m20, d, Math.fma(-m21, b, m22 * a)) * det) + ._properties(0); + } + private Matrix4f invertGenericThis(Matrix4f dest) { + float a = m00 * m11 - m01 * m10; + float b = m00 * m12 - m02 * m10; + float c = m00 * m13 - m03 * m10; + float d = m01 * m12 - m02 * m11; + float e = m01 * m13 - m03 * m11; + float f = m02 * m13 - m03 * m12; + float g = m20 * m31 - m21 * m30; + float h = m20 * m32 - m22 * m30; + float i = m20 * m33 - m23 * m30; + float j = m21 * m32 - m22 * m31; + float k = m21 * m33 - m23 * m31; + float l = m22 * m33 - m23 * m32; + float det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0f / det; + float nm00 = Math.fma( m11, l, Math.fma(-m12, k, m13 * j)) * det; + float nm01 = Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det; + float nm02 = Math.fma( m31, f, Math.fma(-m32, e, m33 * d)) * det; + float nm03 = Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det; + float nm10 = Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det; + float nm11 = Math.fma( m00, l, Math.fma(-m02, i, m03 * h)) * det; + float nm12 = Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det; + float nm13 = Math.fma( m20, f, Math.fma(-m22, c, m23 * b)) * det; + float nm20 = Math.fma( m10, k, Math.fma(-m11, i, m13 * g)) * det; + float nm21 = Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det; + float nm22 = Math.fma( m30, e, Math.fma(-m31, c, m33 * a)) * det; + float nm23 = Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det; + float nm30 = Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det; + float nm31 = Math.fma( m00, j, Math.fma(-m01, h, m02 * g)) * det; + float nm32 = Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det; + float nm33 = Math.fma( m20, d, Math.fma(-m21, b, m22 * a)) * det; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Invert this matrix. + *

+ * If this matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing, + * and thus its last row is equal to (0, 0, 0, 1), then {@link #invertAffine()} can be used instead of this method. + * + * @see #invertAffine() + * + * @return this + */ + public Matrix4f invert() { + return invert(this); + } + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(float, float, float, float) perspective()} methods + * or via {@link #setPerspective(float, float, float, float) setPerspective()}, that is, if this is a symmetrical perspective frustum transformation, + * then this method builds the inverse of this and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix when being obtained via {@link #perspective(float, float, float, float) perspective()}. + * + * @see #perspective(float, float, float, float) + * + * @param dest + * will hold the inverse of this + * @return dest + */ + public Matrix4f invertPerspective(Matrix4f dest) { + float a = 1.0f / (m00 * m11); + float l = -1.0f / (m23 * m32); + return dest + .set(m11 * a, 0, 0, 0, + 0, m00 * a, 0, 0, + 0, 0, 0, -m23 * l, + 0, 0, -m32 * l, m22 * l) + ._properties(0); + } + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(float, float, float, float) perspective()} methods + * or via {@link #setPerspective(float, float, float, float) setPerspective()}, that is, if this is a symmetrical perspective frustum transformation, + * then this method builds the inverse of this. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix when being obtained via {@link #perspective(float, float, float, float) perspective()}. + * + * @see #perspective(float, float, float, float) + * + * @return this + */ + public Matrix4f invertPerspective() { + return invertPerspective(this); + } + + /** + * If this is an arbitrary perspective projection matrix obtained via one of the {@link #frustum(float, float, float, float, float, float) frustum()} methods + * or via {@link #setFrustum(float, float, float, float, float, float) setFrustum()}, + * then this method builds the inverse of this and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix. + *

+ * If this matrix represents a symmetric perspective frustum transformation, as obtained via {@link #perspective(float, float, float, float) perspective()}, then + * {@link #invertPerspective(Matrix4f)} should be used instead. + * + * @see #frustum(float, float, float, float, float, float) + * @see #invertPerspective(Matrix4f) + * + * @param dest + * will hold the inverse of this + * @return dest + */ + public Matrix4f invertFrustum(Matrix4f dest) { + float invM00 = 1.0f / m00; + float invM11 = 1.0f / m11; + float invM23 = 1.0f / m23; + float invM32 = 1.0f / m32; + return dest + .set(invM00, 0, 0, 0, + 0, invM11, 0, 0, + 0, 0, 0, invM32, + -m20 * invM00 * invM23, -m21 * invM11 * invM23, invM23, -m22 * invM23 * invM32); + } + + /** + * If this is an arbitrary perspective projection matrix obtained via one of the {@link #frustum(float, float, float, float, float, float) frustum()} methods + * or via {@link #setFrustum(float, float, float, float, float, float) setFrustum()}, + * then this method builds the inverse of this. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix. + *

+ * If this matrix represents a symmetric perspective frustum transformation, as obtained via {@link #perspective(float, float, float, float) perspective()}, then + * {@link #invertPerspective()} should be used instead. + * + * @see #frustum(float, float, float, float, float, float) + * @see #invertPerspective() + * + * @return this + */ + public Matrix4f invertFrustum() { + return invertFrustum(this); + } + + public Matrix4f invertOrtho(Matrix4f dest) { + float invM00 = 1.0f / m00; + float invM11 = 1.0f / m11; + float invM22 = 1.0f / m22; + return dest + .set(invM00, 0, 0, 0, + 0, invM11, 0, 0, + 0, 0, invM22, 0, + -m30 * invM00, -m31 * invM11, -m32 * invM22, 1) + ._properties(PROPERTY_AFFINE | (this.properties & PROPERTY_ORTHONORMAL)); + } + + /** + * Invert this orthographic projection matrix. + *

+ * This method can be used to quickly obtain the inverse of an orthographic projection matrix. + * + * @return this + */ + public Matrix4f invertOrtho() { + return invertOrtho(this); + } + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(float, float, float, float) perspective()} methods + * or via {@link #setPerspective(float, float, float, float) setPerspective()}, that is, if this is a symmetrical perspective frustum transformation + * and the given view matrix is {@link #isAffine() affine} and has unit scaling (for example by being obtained via {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt()}), + * then this method builds the inverse of this * view and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of the combination of the view and projection matrices, when both were obtained + * via the common methods {@link #perspective(float, float, float, float) perspective()} and {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt()} or + * other methods, that build affine matrices, such as {@link #translate(float, float, float) translate} and {@link #rotate(float, float, float, float)}, except for {@link #scale(float, float, float) scale()}. + *

+ * For the special cases of the matrices this and view mentioned above, this method is equivalent to the following code: + *

+     * dest.set(this).mul(view).invert();
+     * 
+ * + * @param view + * the view transformation (must be {@link #isAffine() affine} and have unit scaling) + * @param dest + * will hold the inverse of this * view + * @return dest + */ + public Matrix4f invertPerspectiveView(Matrix4fc view, Matrix4f dest) { + float a = 1.0f / (m00 * m11); + float l = -1.0f / (m23 * m32); + float pm00 = m11 * a; + float pm11 = m00 * a; + float pm23 = -m23 * l; + float pm32 = -m32 * l; + float pm33 = m22 * l; + float vm30 = -view.m00() * view.m30() - view.m01() * view.m31() - view.m02() * view.m32(); + float vm31 = -view.m10() * view.m30() - view.m11() * view.m31() - view.m12() * view.m32(); + float vm32 = -view.m20() * view.m30() - view.m21() * view.m31() - view.m22() * view.m32(); + float nm10 = view.m01() * pm11; + float nm30 = view.m02() * pm32 + vm30 * pm33; + float nm31 = view.m12() * pm32 + vm31 * pm33; + float nm32 = view.m22() * pm32 + vm32 * pm33; + return dest + ._m00(view.m00() * pm00) + ._m01(view.m10() * pm00) + ._m02(view.m20() * pm00) + ._m03(0.0f) + ._m10(nm10) + ._m11(view.m11() * pm11) + ._m12(view.m21() * pm11) + ._m13(0.0f) + ._m20(vm30 * pm23) + ._m21(vm31 * pm23) + ._m22(vm32 * pm23) + ._m23(pm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(pm33) + ._properties(0); + } + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(float, float, float, float) perspective()} methods + * or via {@link #setPerspective(float, float, float, float) setPerspective()}, that is, if this is a symmetrical perspective frustum transformation + * and the given view matrix has unit scaling, + * then this method builds the inverse of this * view and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of the combination of the view and projection matrices, when both were obtained + * via the common methods {@link #perspective(float, float, float, float) perspective()} and {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt()} or + * other methods, that build affine matrices, such as {@link #translate(float, float, float) translate} and {@link #rotate(float, float, float, float)}, except for {@link #scale(float, float, float) scale()}. + *

+ * For the special cases of the matrices this and view mentioned above, this method is equivalent to the following code: + *

+     * dest.set(this).mul(view).invert();
+     * 
+ * + * @param view + * the view transformation (must have unit scaling) + * @param dest + * will hold the inverse of this * view + * @return dest + */ + public Matrix4f invertPerspectiveView(Matrix4x3fc view, Matrix4f dest) { + float a = 1.0f / (m00 * m11); + float l = -1.0f / (m23 * m32); + float pm00 = m11 * a; + float pm11 = m00 * a; + float pm23 = -m23 * l; + float pm32 = -m32 * l; + float pm33 = m22 * l; + float vm30 = -view.m00() * view.m30() - view.m01() * view.m31() - view.m02() * view.m32(); + float vm31 = -view.m10() * view.m30() - view.m11() * view.m31() - view.m12() * view.m32(); + float vm32 = -view.m20() * view.m30() - view.m21() * view.m31() - view.m22() * view.m32(); + return dest + ._m00(view.m00() * pm00) + ._m01(view.m10() * pm00) + ._m02(view.m20() * pm00) + ._m03(0.0f) + ._m10(view.m01() * pm11) + ._m11(view.m11() * pm11) + ._m12(view.m21() * pm11) + ._m13(0.0f) + ._m20(vm30 * pm23) + ._m21(vm31 * pm23) + ._m22(vm32 * pm23) + ._m23(pm23) + ._m30(view.m02() * pm32 + vm30 * pm33) + ._m31(view.m12() * pm32 + vm31 * pm33) + ._m32(view.m22() * pm32 + vm32 * pm33) + ._m33(pm33) + ._properties(0); + } + + public Matrix4f invertAffine(Matrix4f dest) { + float m11m00 = m00 * m11, m10m01 = m01 * m10, m10m02 = m02 * m10; + float m12m00 = m00 * m12, m12m01 = m01 * m12, m11m02 = m02 * m11; + float det = (m11m00 - m10m01) * m22 + (m10m02 - m12m00) * m21 + (m12m01 - m11m02) * m20; + float s = 1.0f / det; + float m10m22 = m10 * m22, m10m21 = m10 * m21, m11m22 = m11 * m22; + float m11m20 = m11 * m20, m12m21 = m12 * m21, m12m20 = m12 * m20; + float m20m02 = m20 * m02, m20m01 = m20 * m01, m21m02 = m21 * m02; + float m21m00 = m21 * m00, m22m01 = m22 * m01, m22m00 = m22 * m00; + float nm31 = (m20m02 * m31 - m20m01 * m32 + m21m00 * m32 - m21m02 * m30 + m22m01 * m30 - m22m00 * m31) * s; + float nm32 = (m11m02 * m30 - m12m01 * m30 + m12m00 * m31 - m10m02 * m31 + m10m01 * m32 - m11m00 * m32) * s; + return dest + ._m00((m11m22 - m12m21) * s) + ._m01((m21m02 - m22m01) * s) + ._m02((m12m01 - m11m02) * s) + ._m03(0.0f) + ._m10((m12m20 - m10m22) * s) + ._m11((m22m00 - m20m02) * s) + ._m12((m10m02 - m12m00) * s) + ._m13(0.0f) + ._m20((m10m21 - m11m20) * s) + ._m21((m20m01 - m21m00) * s) + ._m22((m11m00 - m10m01) * s) + ._m23(0.0f) + ._m30((m10m22 * m31 - m10m21 * m32 + m11m20 * m32 - m11m22 * m30 + m12m21 * m30 - m12m20 * m31) * s) + ._m31(nm31) + ._m32(nm32) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE); + } + + /** + * Invert this matrix by assuming that it is an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)). + * + * @return this + */ + public Matrix4f invertAffine() { + return invertAffine(this); + } + + public Matrix4f transpose(Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if (this != dest) + return transposeNonThisGeneric(dest); + return transposeThisGeneric(dest); + } + private Matrix4f transposeNonThisGeneric(Matrix4f dest) { + return dest + ._m00(m00) + ._m01(m10) + ._m02(m20) + ._m03(m30) + ._m10(m01) + ._m11(m11) + ._m12(m21) + ._m13(m31) + ._m20(m02) + ._m21(m12) + ._m22(m22) + ._m23(m32) + ._m30(m03) + ._m31(m13) + ._m32(m23) + ._m33(m33) + ._properties(0); + } + private Matrix4f transposeThisGeneric(Matrix4f dest) { + float nm10 = m01; + float nm20 = m02; + float nm21 = m12; + float nm30 = m03; + float nm31 = m13; + float nm32 = m23; + return dest + ._m01(m10) + ._m02(m20) + ._m03(m30) + ._m10(nm10) + ._m12(m21) + ._m13(m31) + ._m20(nm20) + ._m21(nm21) + ._m23(m32) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._properties(0); + } + + /** + * Transpose only the upper left 3x3 submatrix of this matrix. + *

+ * All other matrix elements are left unchanged. + * + * @return this + */ + public Matrix4f transpose3x3() { + return transpose3x3(this); + } + + public Matrix4f transpose3x3(Matrix4f dest) { + float nm10 = m01, nm20 = m02, nm21 = m12; + return dest + ._m00(m00) + ._m01(m10) + ._m02(m20) + ._m10(nm10) + ._m11(m11) + ._m12(m21) + ._m20(nm20) + ._m21(nm21) + ._m22(m22) + ._properties(this.properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + public Matrix3f transpose3x3(Matrix3f dest) { + return dest + ._m00(m00) + ._m01(m10) + ._m02(m20) + ._m10(m01) + ._m11(m11) + ._m12(m21) + ._m20(m02) + ._m21(m12) + ._m22(m22); + } + + /** + * Transpose this matrix. + * + * @return this + */ + public Matrix4f transpose() { + return transpose(this); + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + *

+ * In order to post-multiply a translation transformation directly to a + * matrix, use {@link #translate(float, float, float) translate()} instead. + * + * @see #translate(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4f translation(float x, float y, float z) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + return this._m30(x)._m31(y)._m32(z)._properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + *

+ * In order to post-multiply a translation transformation directly to a + * matrix, use {@link #translate(Vector3fc) translate()} instead. + * + * @see #translate(float, float, float) + * + * @param offset + * the offsets in x, y and z to translate + * @return this + */ + public Matrix4f translation(Vector3fc offset) { + return translation(offset.x(), offset.y(), offset.z()); + } + + /** + * Set only the translation components (m30, m31, m32) of this matrix to the given values (x, y, z). + *

+ * Note that this will only work properly for orthogonal matrices (without any perspective). + *

+ * To build a translation matrix instead, use {@link #translation(float, float, float)}. + * To apply a translation, use {@link #translate(float, float, float)}. + * + * @see #translation(float, float, float) + * @see #translate(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4f setTranslation(float x, float y, float z) { + return this._m30(x)._m31(y)._m32(z)._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY)); + } + + /** + * Set only the translation components (m30, m31, m32) of this matrix to the values (xyz.x, xyz.y, xyz.z). + *

+ * Note that this will only work properly for orthogonal matrices (without any perspective). + *

+ * To build a translation matrix instead, use {@link #translation(Vector3fc)}. + * To apply a translation, use {@link #translate(Vector3fc)}. + * + * @see #translation(Vector3fc) + * @see #translate(Vector3fc) + * + * @param xyz + * the units to translate in (x, y, z) + * @return this + */ + public Matrix4f setTranslation(Vector3fc xyz) { + return setTranslation(xyz.x(), xyz.y(), xyz.z()); + } + + public Vector3f getTranslation(Vector3f dest) { + dest.x = m30; + dest.y = m31; + dest.z = m32; + return dest; + } + + public Vector3f getScale(Vector3f dest) { + dest.x = Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02); + dest.y = Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12); + dest.z = Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22); + return dest; + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + " " + Runtime.format(m20, formatter) + " " + Runtime.format(m30, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + " " + Runtime.format(m21, formatter) + " " + Runtime.format(m31, formatter) + "\n" + + Runtime.format(m02, formatter) + " " + Runtime.format(m12, formatter) + " " + Runtime.format(m22, formatter) + " " + Runtime.format(m32, formatter) + "\n" + + Runtime.format(m03, formatter) + " " + Runtime.format(m13, formatter) + " " + Runtime.format(m23, formatter) + " " + Runtime.format(m33, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix4fc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix4fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix4f get(Matrix4f dest) { + return dest.set(this); + } + + public Matrix4x3f get4x3(Matrix4x3f dest) { + return dest.set(this); + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix4dc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix4dc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix4d get(Matrix4d dest) { + return dest.set(this); + } + + public Matrix3f get3x3(Matrix3f dest) { + return dest.set(this); + } + + public Matrix3d get3x3(Matrix3d dest) { + return dest.set(this); + } + + public AxisAngle4f getRotation(AxisAngle4f dest) { + return dest.set(this); + } + + public AxisAngle4d getRotation(AxisAngle4d dest) { + return dest.set(this); + } + + public Quaternionf getUnnormalizedRotation(Quaternionf dest) { + return dest.setFromUnnormalized(this); + } + + public Quaternionf getNormalizedRotation(Quaternionf dest) { + return dest.setFromNormalized(this); + } + + public Quaterniond getUnnormalizedRotation(Quaterniond dest) { + return dest.setFromUnnormalized(this); + } + + public Quaterniond getNormalizedRotation(Quaterniond dest) { + return dest.setFromNormalized(this); + } + + + public FloatBuffer get(FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public FloatBuffer get4x3(FloatBuffer buffer) { + MemUtil.INSTANCE.put4x3(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get4x3(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put4x3(this, index, buffer); + return buffer; + } + + public ByteBuffer get4x3(ByteBuffer buffer) { + MemUtil.INSTANCE.put4x3(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get4x3(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put4x3(this, index, buffer); + return buffer; + } + + public FloatBuffer get3x4(FloatBuffer buffer) { + MemUtil.INSTANCE.put3x4(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get3x4(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put3x4(this, index, buffer); + return buffer; + } + + public ByteBuffer get3x4(ByteBuffer buffer) { + MemUtil.INSTANCE.put3x4(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get3x4(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put3x4(this, index, buffer); + return buffer; + } + + public FloatBuffer getTransposed(FloatBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer getTransposed(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public ByteBuffer getTransposed(ByteBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer getTransposed(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public FloatBuffer get4x3Transposed(FloatBuffer buffer) { + MemUtil.INSTANCE.put4x3Transposed(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get4x3Transposed(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put4x3Transposed(this, index, buffer); + return buffer; + } + + public ByteBuffer get4x3Transposed(ByteBuffer buffer) { + MemUtil.INSTANCE.put4x3Transposed(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get4x3Transposed(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put4x3Transposed(this, index, buffer); + return buffer; + } + public Matrix4fc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + public float[] get(float[] arr, int offset) { + MemUtil.INSTANCE.copy(this, arr, offset); + return arr; + } + + public float[] get(float[] arr) { + MemUtil.INSTANCE.copy(this, arr, 0); + return arr; + } + + /** + * Set all the values within this matrix to 0. + * + * @return this + */ + public Matrix4f zero() { + MemUtil.INSTANCE.zero(this); + return _properties(0); + } + + /** + * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(float) scale()} instead. + * + * @see #scale(float) + * + * @param factor + * the scale factor in x, y and z + * @return this + */ + public Matrix4f scaling(float factor) { + return scaling(factor, factor, factor); + } + + /** + * Set this matrix to be a simple scale matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(float, float, float) scale()} instead. + * + * @see #scale(float, float, float) + * + * @param x + * the scale in x + * @param y + * the scale in y + * @param z + * the scale in z + * @return this + */ + public Matrix4f scaling(float x, float y, float z) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + boolean one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z); + return this + ._m00(x) + ._m11(y) + ._m22(z) + ._properties(PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0)); + } + + /** + * Set this matrix to be a simple scale matrix which scales the base axes by xyz.x, xyz.y and xyz.z respectively. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix use {@link #scale(Vector3fc) scale()} instead. + * + * @see #scale(Vector3fc) + * + * @param xyz + * the scale in x, y and z respectively + * @return this + */ + public Matrix4f scaling(Vector3fc xyz) { + return scaling(xyz.x(), xyz.y(), xyz.z()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to post-multiply a rotation transformation directly to a + * matrix, use {@link #rotate(float, Vector3fc) rotate()} instead. + * + * @see #rotate(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix4f rotation(float angle, Vector3fc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4f}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4f) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix4f rotation(AxisAngle4f axisAngle) { + return rotation(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(float, float, float, float) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * + * @param angle + * the angle in radians + * @param x + * the x-component of the rotation axis + * @param y + * the y-component of the rotation axis + * @param z + * the z-component of the rotation axis + * @return this + */ + public Matrix4f rotation(float angle, float x, float y, float z) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotationX(x * angle); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotationY(y * angle); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotationZ(z * angle); + return rotationInternal(angle, x, y, z); + } + private Matrix4f rotationInternal(float angle, float x, float y, float z) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float C = 1.0f - cos, xy = x * y, xz = x * z, yz = y * z; + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + return this + ._m00(cos + x * x * C) + ._m10(xy * C - z * sin) + ._m20(xz * C + y * sin) + ._m01(xy * C + z * sin) + ._m11(cos + y * y * C) + ._m21(yz * C - x * sin) + ._m02(xz * C - y * sin) + ._m12(yz * C + x * sin) + ._m22(cos + z * z * C) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to a rotation transformation about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4f rotationX(float ang) { + float sin = Math.sin(ang), cos = Math.cosFromSin(sin, ang); + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m11(cos)._m12(sin)._m21(-sin)._m22(cos)._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to a rotation transformation about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4f rotationY(float ang) { + float sin = Math.sin(ang), cos = Math.cosFromSin(sin, ang); + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00(cos)._m02(-sin)._m20(sin)._m22(cos)._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to a rotation transformation about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4f rotationZ(float ang) { + float sin = Math.sin(ang), cos = Math.cosFromSin(sin, ang); + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + return this._m00(cos)._m01(sin)._m10(-sin)._m11(cos)._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to a rotation transformation about the Z axis to align the local +X towards (dirX, dirY). + *

+ * The vector (dirX, dirY) must be a unit vector. + * + * @param dirX + * the x component of the normalized direction + * @param dirY + * the y component of the normalized direction + * @return this + */ + public Matrix4f rotationTowardsXY(float dirX, float dirY) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + return this._m00(dirY)._m01(dirX)._m10(-dirX)._m11(dirY)._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4f rotationXYZ(float angleX, float angleY, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + float nm01 = -sinX * -sinY, nm02 = cosX * -sinY; + return this + ._m20(sinY) + ._m21(-sinX * cosY) + ._m22(cosX * cosY) + ._m00(cosY * cosZ) + ._m01(nm01 * cosZ + cosX * sinZ) + ._m02(nm02 * cosZ + sinX * sinZ) + ._m10(cosY * -sinZ) + ._m11(nm01 * -sinZ + cosX * cosZ) + ._m12(nm02 * -sinZ + sinX * cosZ) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4f rotationZYX(float angleZ, float angleY, float angleX) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float nm20 = cosZ * sinY; + float nm21 = sinZ * sinY; + return this + ._m00(cosZ * cosY) + ._m01(sinZ * cosY) + ._m02(-sinY) + ._m03(0.0f) + ._m10(-sinZ * cosX + nm20 * sinX) + ._m11(cosZ * cosX + nm21 * sinX) + ._m12(cosY * sinX) + ._m13(0.0f) + ._m20(-sinZ * -sinX + nm20 * cosX) + ._m21(cosZ * -sinX + nm21 * cosX) + ._m22(cosY * cosX) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4f rotationYXZ(float angleY, float angleX, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float nm10 = sinY * sinX, nm12 = cosY * sinX; + return this + ._m20(sinY * cosX) + ._m21(-sinX) + ._m22(cosY * cosX) + ._m23(0.0f) + ._m00(cosY * cosZ + nm10 * sinZ) + ._m01(cosX * sinZ) + ._m02(-sinY * cosZ + nm12 * sinZ) + ._m03(0.0f) + ._m10(cosY * -sinZ + nm10 * cosZ) + ._m11(cosX * cosZ) + ._m12(-sinY * -sinZ + nm12 * cosZ) + ._m13(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set only the upper left 3x3 submatrix of this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4f setRotationXYZ(float angleX, float angleY, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float nm01 = -sinX * -sinY; + float nm02 = cosX * -sinY; + return this + ._m20(sinY) + ._m21(-sinX * cosY) + ._m22(cosX * cosY) + ._m00(cosY * cosZ) + ._m01(nm01 * cosZ + cosX * sinZ) + ._m02(nm02 * cosZ + sinX * sinZ) + ._m10(cosY * -sinZ) + ._m11(nm01 * -sinZ + cosX * cosZ) + ._m12(nm02 * -sinZ + sinX * cosZ) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Set only the upper left 3x3 submatrix of this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4f setRotationZYX(float angleZ, float angleY, float angleX) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float nm20 = cosZ * sinY, nm21 = sinZ * sinY; + return this + ._m00(cosZ * cosY) + ._m01(sinZ * cosY) + ._m02(-sinY) + ._m10(-sinZ * cosX + nm20 * sinX) + ._m11(cosZ * cosX + nm21 * sinX) + ._m12(cosY * sinX) + ._m20(-sinZ * -sinX + nm20 * cosX) + ._m21(cosZ * -sinX + nm21 * cosX) + ._m22(cosY * cosX) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Set only the upper left 3x3 submatrix of this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4f setRotationYXZ(float angleY, float angleX, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float nm10 = sinY * sinX, nm12 = cosY * sinX; + return this + ._m20(sinY * cosX) + ._m21(-sinX) + ._m22(cosY * cosX) + ._m00(cosY * cosZ + nm10 * sinZ) + ._m01(cosX * sinZ) + ._m02(-sinY * cosZ + nm12 * sinZ) + ._m10(cosY * -sinZ + nm10 * cosZ) + ._m11(cosX * cosZ) + ._m12(-sinY * -sinZ + nm12 * cosZ) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Set this matrix to the rotation transformation of the given {@link Quaternionfc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaternionfc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4f rotation(Quaternionfc quat) { + float w2 = quat.w() * quat.w(); + float x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(); + float z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw; + float xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz; + float yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz; + float xw = quat.x() * quat.w(), dxw = xw + xw; + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + return this + ._m00(w2 + x2 - z2 - y2) + ._m01(dxy + dzw) + ._m02(dxz - dyw) + ._m10(-dzw + dxy) + ._m11(y2 - z2 + w2 - x2) + ._m12(dyz + dxw) + ._m20(dyw + dxz) + ._m21(dyz - dxw) + ._m22(z2 - y2 - x2 + w2) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to T * R * S, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), and S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz). + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * @see #scale(float, float, float) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @return this + */ + public Matrix4f translationRotateScale(float tx, float ty, float tz, + float qx, float qy, float qz, float qw, + float sx, float sy, float sz) { + float dqx = qx + qx; + float dqy = qy + qy; + float dqz = qz + qz; + float q00 = dqx * qx; + float q11 = dqy * qy; + float q22 = dqz * qz; + float q01 = dqx * qy; + float q02 = dqx * qz; + float q03 = dqx * qw; + float q12 = dqy * qz; + float q13 = dqy * qw; + float q23 = dqz * qw; + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + return this + ._m00(sx - (q11 + q22) * sx) + ._m01((q01 + q23) * sx) + ._m02((q02 - q13) * sx) + ._m03(0.0f) + ._m10((q01 - q23) * sy) + ._m11(sy - (q22 + q00) * sy) + ._m12((q12 + q03) * sy) + ._m13(0.0f) + ._m20((q02 + q13) * sz) + ._m21((q12 - q03) * sz) + ._m22(sz - (q11 + q00) * sz) + ._m23(0.0f) + ._m30(tx) + ._m31(ty) + ._m32(tz) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0)); + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaternionfc) + * @see #scale(Vector3fc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4f translationRotateScale(Vector3fc translation, + Quaternionfc quat, + Vector3fc scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to T * R * S, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), and S is a scaling transformation + * which scales all three axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(scale) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * @see #scale(float) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param scale + * the scaling factor for all three axes + * @return this + */ + public Matrix4f translationRotateScale(float tx, float ty, float tz, + float qx, float qy, float qz, float qw, + float scale) { + return translationRotateScale(tx, ty, tz, qx, qy, qz, qw, scale, scale, scale); + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales all three axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaternionfc) + * @see #scale(float) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4f translationRotateScale(Vector3fc translation, + Quaternionfc quat, + float scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale, scale, scale); + } + + /** + * Set this matrix to (T * R * S)-1, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), and S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz). + *

+ * This method is equivalent to calling: translationRotateScale(...).invert() + * + * @see #translationRotateScale(float, float, float, float, float, float, float, float, float, float) + * @see #invert() + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @return this + */ + public Matrix4f translationRotateScaleInvert(float tx, float ty, float tz, + float qx, float qy, float qz, float qw, + float sx, float sy, float sz) { + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + if (one) + return translationRotateScale(tx, ty, tz, qx, qy, qz, qw, sx, sy, sz).invertOrthonormal(this); + float nqx = -qx, nqy = -qy, nqz = -qz; + float dqx = nqx + nqx; + float dqy = nqy + nqy; + float dqz = nqz + nqz; + float q00 = dqx * nqx; + float q11 = dqy * nqy; + float q22 = dqz * nqz; + float q01 = dqx * nqy; + float q02 = dqx * nqz; + float q03 = dqx * qw; + float q12 = dqy * nqz; + float q13 = dqy * qw; + float q23 = dqz * qw; + float isx = 1/sx, isy = 1/sy, isz = 1/sz; + return this + ._m00(isx * (1.0f - q11 - q22)) + ._m01(isy * (q01 + q23)) + ._m02(isz * (q02 - q13)) + ._m03(0.0f) + ._m10(isx * (q01 - q23)) + ._m11(isy * (1.0f - q22 - q00)) + ._m12(isz * (q12 + q03)) + ._m13(0.0f) + ._m20(isx * (q02 + q13)) + ._m21(isy * (q12 - q03)) + ._m22(isz * (1.0f - q11 - q00)) + ._m23(0.0f) + ._m30(-m00 * tx - m10 * ty - m20 * tz) + ._m31(-m01 * tx - m11 * ty - m21 * tz) + ._m32(-m02 * tx - m12 * ty - m22 * tz) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE); + } + + /** + * Set this matrix to (T * R * S)-1, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * This method is equivalent to calling: translationRotateScale(...).invert() + * + * @see #translationRotateScale(Vector3fc, Quaternionfc, Vector3fc) + * @see #invert() + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4f translationRotateScaleInvert(Vector3fc translation, + Quaternionfc quat, + Vector3fc scale) { + return translationRotateScaleInvert(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to (T * R * S)-1, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales all three axes by scale. + *

+ * This method is equivalent to calling: translationRotateScale(...).invert() + * + * @see #translationRotateScale(Vector3fc, Quaternionfc, float) + * @see #invert() + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4f translationRotateScaleInvert(Vector3fc translation, + Quaternionfc quat, + float scale) { + return translationRotateScaleInvert(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale, scale, scale); + } + + /** + * Set this matrix to T * R * S * M, where T is a translation by the given (tx, ty, tz), + * R is a rotation - and possibly scaling - transformation specified by the quaternion (qx, qy, qz, qw), S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz) and M is an {@link #isAffine() affine} matrix. + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz).mulAffine(m) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * @see #scale(float, float, float) + * @see #mulAffine(Matrix4fc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @param m + * the {@link #isAffine() affine} matrix to multiply by + * @return this + */ + public Matrix4f translationRotateScaleMulAffine(float tx, float ty, float tz, + float qx, float qy, float qz, float qw, + float sx, float sy, float sz, + Matrix4f m) { + float w2 = qw * qw; + float x2 = qx * qx; + float y2 = qy * qy; + float z2 = qz * qz; + float zw = qz * qw; + float xy = qx * qy; + float xz = qx * qz; + float yw = qy * qw; + float yz = qy * qz; + float xw = qx * qw; + float nm00 = w2 + x2 - z2 - y2; + float nm01 = xy + zw + zw + xy; + float nm02 = xz - yw + xz - yw; + float nm10 = -zw + xy - zw + xy; + float nm11 = y2 - z2 + w2 - x2; + float nm12 = yz + yz + xw + xw; + float nm20 = yw + xz + xz + yw; + float nm21 = yz + yz - xw - xw; + float nm22 = z2 - y2 - x2 + w2; + float m00 = nm00 * m.m00 + nm10 * m.m01 + nm20 * m.m02; + float m01 = nm01 * m.m00 + nm11 * m.m01 + nm21 * m.m02; + this._m02(nm02 * m.m00 + nm12 * m.m01 + nm22 * m.m02) + ._m00(m00) + ._m01(m01) + ._m03(0.0f); + float m10 = nm00 * m.m10 + nm10 * m.m11 + nm20 * m.m12; + float m11 = nm01 * m.m10 + nm11 * m.m11 + nm21 * m.m12; + this._m12(nm02 * m.m10 + nm12 * m.m11 + nm22 * m.m12) + ._m10(m10) + ._m11(m11) + ._m13(0.0f); + float m20 = nm00 * m.m20 + nm10 * m.m21 + nm20 * m.m22; + float m21 = nm01 * m.m20 + nm11 * m.m21 + nm21 * m.m22; + this._m22(nm02 * m.m20 + nm12 * m.m21 + nm22 * m.m22) + ._m20(m20) + ._m21(m21) + ._m23(0.0f); + float m30 = nm00 * m.m30 + nm10 * m.m31 + nm20 * m.m32 + tx; + float m31 = nm01 * m.m30 + nm11 * m.m31 + nm21 * m.m32 + ty; + this._m32(nm02 * m.m30 + nm12 * m.m31 + nm22 * m.m32 + tz) + ._m30(m30) + ._m31(m31) + ._m33(1.0f); + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + return _properties(PROPERTY_AFFINE | (one && (m.properties & PROPERTY_ORTHONORMAL) != 0 ? PROPERTY_ORTHONORMAL : 0)); + } + + /** + * Set this matrix to T * R * S * M, where T is the given translation, + * R is a rotation - and possibly scaling - transformation specified by the given quaternion, S is a scaling transformation + * which scales the axes by scale and M is an {@link #isAffine() affine} matrix. + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale).mulAffine(m) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaternionfc) + * @see #mulAffine(Matrix4fc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @param m + * the {@link #isAffine() affine} matrix to multiply by + * @return this + */ + public Matrix4f translationRotateScaleMulAffine(Vector3fc translation, + Quaternionfc quat, + Vector3fc scale, + Matrix4f m) { + return translationRotateScaleMulAffine(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z(), m); + } + + /** + * Set this matrix to T * R, where T is a translation by the given (tx, ty, tz) and + * R is a rotation - and possibly scaling - transformation specified by the quaternion (qx, qy, qz, qw). + *

+ * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @return this + */ + public Matrix4f translationRotate(float tx, float ty, float tz, float qx, float qy, float qz, float qw) { + float w2 = qw * qw; + float x2 = qx * qx; + float y2 = qy * qy; + float z2 = qz * qz; + float zw = qz * qw; + float xy = qx * qy; + float xz = qx * qz; + float yw = qy * qw; + float yz = qy * qz; + float xw = qx * qw; + return this + ._m00(w2 + x2 - z2 - y2) + ._m01(xy + zw + zw + xy) + ._m02(xz - yw + xz - yw) + ._m10(-zw + xy - zw + xy) + ._m11(y2 - z2 + w2 - x2) + ._m12(yz + yz + xw + xw) + ._m20(yw + xz + xz + yw) + ._m21(yz + yz - xw - xw) + ._m22(z2 - y2 - x2 + w2) + ._m30(tx) + ._m31(ty) + ._m32(tz) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Set this matrix to T * R, where T is a translation by the given (tx, ty, tz) and + * R is a rotation - and possibly scaling - transformation specified by the given quaternion. + *

+ * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param quat + * the quaternion representing a rotation + * @return this + */ + public Matrix4f translationRotate(float tx, float ty, float tz, Quaternionfc quat) { + return translationRotate(tx, ty, tz, quat.x(), quat.y(), quat.z(), quat.w()); + } + + /** + * Set the upper left 3x3 submatrix of this {@link Matrix4f} to the given {@link Matrix3fc} and don't change the other elements. + * + * @param mat + * the 3x3 matrix + * @return this + */ + public Matrix4f set3x3(Matrix3fc mat) { + return + set3x3Matrix3fc(mat). + _properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + } + private Matrix4f set3x3Matrix3fc(Matrix3fc mat) { + return this + ._m00(mat.m00()) + ._m01(mat.m01()) + ._m02(mat.m02()) + ._m10(mat.m10()) + ._m11(mat.m11()) + ._m12(mat.m12()) + ._m20(mat.m20()) + ._m21(mat.m21()) + ._m22(mat.m22()); + } + + public Vector4f transform(Vector4f v) { + return v.mul(this); + } + + public Vector4f transform(Vector4fc v, Vector4f dest) { + return v.mul(this, dest); + } + + public Vector4f transform(float x, float y, float z, float w, Vector4f dest) { + return dest.set(x, y, z, w).mul(this); + } + + public Vector4f transformTranspose(Vector4f v) { + return v.mulTranspose(this); + } + public Vector4f transformTranspose(Vector4fc v, Vector4f dest) { + return v.mulTranspose(this, dest); + } + public Vector4f transformTranspose(float x, float y, float z, float w, Vector4f dest) { + return dest.set(x, y, z, w).mulTranspose(this); + } + + public Vector4f transformProject(Vector4f v) { + return v.mulProject(this); + } + + public Vector4f transformProject(Vector4fc v, Vector4f dest) { + return v.mulProject(this, dest); + } + + public Vector4f transformProject(float x, float y, float z, float w, Vector4f dest) { + return dest.set(x, y, z, w).mulProject(this); + } + + public Vector3f transformProject(Vector4fc v, Vector3f dest) { + return v.mulProject(this, dest); + } + + public Vector3f transformProject(float x, float y, float z, float w, Vector3f dest) { + return dest.set(x, y, z).mulProject(this, w, dest); + } + + public Vector3f transformProject(Vector3f v) { + return v.mulProject(this); + } + + public Vector3f transformProject(Vector3fc v, Vector3f dest) { + return v.mulProject(this, dest); + } + + public Vector3f transformProject(float x, float y, float z, Vector3f dest) { + return dest.set(x, y, z).mulProject(this); + } + + public Vector3f transformPosition(Vector3f v) { + return v.mulPosition(this); + } + + public Vector3f transformPosition(Vector3fc v, Vector3f dest) { + return transformPosition(v.x(), v.y(), v.z(), dest); + } + + public Vector3f transformPosition(float x, float y, float z, Vector3f dest) { + return dest.set(x, y, z).mulPosition(this); + } + + public Vector3f transformDirection(Vector3f v) { + return transformDirection(v.x, v.y, v.z, v); + } + + public Vector3f transformDirection(Vector3fc v, Vector3f dest) { + return transformDirection(v.x(), v.y(), v.z(), dest); + } + + public Vector3f transformDirection(float x, float y, float z, Vector3f dest) { + return dest.set(x, y, z).mulDirection(this); + } + + public Vector4f transformAffine(Vector4f v) { + return v.mulAffine(this, v); + } + + public Vector4f transformAffine(Vector4fc v, Vector4f dest) { + return transformAffine(v.x(), v.y(), v.z(), v.w(), dest); + } + + public Vector4f transformAffine(float x, float y, float z, float w, Vector4f dest) { + return dest.set(x, y, z, w).mulAffine(this, dest); + } + + public Matrix4f scale(Vector3fc xyz, Matrix4f dest) { + return scale(xyz.x(), xyz.y(), xyz.z(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @return this + */ + public Matrix4f scale(Vector3fc xyz) { + return scale(xyz.x(), xyz.y(), xyz.z(), this); + } + + public Matrix4f scale(float xyz, Matrix4f dest) { + return scale(xyz, xyz, xyz, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * Individual scaling of all three axes can be applied using {@link #scale(float, float, float)}. + * + * @see #scale(float, float, float) + * + * @param xyz + * the factor for all components + * @return this + */ + public Matrix4f scale(float xyz) { + return scale(xyz, xyz, xyz); + } + + public Matrix4f scaleXY(float x, float y, Matrix4f dest) { + return scale(x, y, 1.0f, dest); + } + + /** + * Apply scaling to this matrix by scaling the X axis by x and the Y axis by y. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix4f scaleXY(float x, float y) { + return scale(x, y, 1.0f); + } + + public Matrix4f scale(float x, float y, float z, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.scaling(x, y, z); + return scaleGeneric(x, y, z, dest); + } + private Matrix4f scaleGeneric(float x, float y, float z, Matrix4f dest) { + boolean one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z); + return dest + ._m00(m00 * x) + ._m01(m01 * x) + ._m02(m02 * x) + ._m03(m03 * x) + ._m10(m10 * y) + ._m11(m11 * y) + ._m12(m12 * y) + ._m13(m13 * y) + ._m20(m20 * z) + ._m21(m21 * z) + ._m22(m22 * z) + ._m23(m23 * z) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | (one ? 0 : PROPERTY_ORTHONORMAL))); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix4f scale(float x, float y, float z) { + return scale(x, y, z, this); + } + + public Matrix4f scaleAround(float sx, float sy, float sz, float ox, float oy, float oz, Matrix4f dest) { + float nm30 = m00 * ox + m10 * oy + m20 * oz + m30; + float nm31 = m01 * ox + m11 * oy + m21 * oz + m31; + float nm32 = m02 * ox + m12 * oy + m22 * oz + m32; + float nm33 = m03 * ox + m13 * oy + m23 * oz + m33; + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + return dest + ._m00(m00 * sx) + ._m01(m01 * sx) + ._m02(m02 * sx) + ._m03(m03 * sx) + ._m10(m10 * sy) + ._m11(m11 * sy) + ._m12(m12 * sy) + ._m13(m13 * sy) + ._m20(m20 * sz) + ._m21(m21 * sz) + ._m22(m22 * sz) + ._m23(m23 * sz) + ._m30(-dest.m00 * ox - dest.m10 * oy - dest.m20 * oz + nm30) + ._m31(-dest.m01 * ox - dest.m11 * oy - dest.m21 * oz + nm31) + ._m32(-dest.m02 * ox - dest.m12 * oy - dest.m22 * oz + nm32) + ._m33(-dest.m03 * ox - dest.m13 * oy - dest.m23 * oz + nm33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | (one ? 0 : PROPERTY_ORTHONORMAL))); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4f scaleAround(float sx, float sy, float sz, float ox, float oy, float oz) { + return scaleAround(sx, sy, sz, ox, oy, oz, this); + } + + /** + * Apply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4f scaleAround(float factor, float ox, float oy, float oz) { + return scaleAround(factor, factor, factor, ox, oy, oz, this); + } + + public Matrix4f scaleAround(float factor, float ox, float oy, float oz, Matrix4f dest) { + return scaleAround(factor, factor, factor, ox, oy, oz, dest); + } + + public Matrix4f scaleLocal(float x, float y, float z, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.scaling(x, y, z); + return scaleLocalGeneric(x, y, z, dest); + } + private Matrix4f scaleLocalGeneric(float x, float y, float z, Matrix4f dest) { + float nm00 = x * m00; + float nm01 = y * m01; + float nm02 = z * m02; + float nm10 = x * m10; + float nm11 = y * m11; + float nm12 = z * m12; + float nm20 = x * m20; + float nm21 = y * m21; + float nm22 = z * m22; + float nm30 = x * m30; + float nm31 = y * m31; + float nm32 = z * m32; + boolean one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z); + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(m03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(m13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | (one ? 0 : PROPERTY_ORTHONORMAL))); + } + + public Matrix4f scaleLocal(float xyz, Matrix4f dest) { + return scaleLocal(xyz, xyz, xyz, dest); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param xyz + * the factor of the x, y and z component + * @return this + */ + public Matrix4f scaleLocal(float xyz) { + return scaleLocal(xyz, this); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix4f scaleLocal(float x, float y, float z) { + return scaleLocal(x, y, z, this); + } + + public Matrix4f scaleAroundLocal(float sx, float sy, float sz, float ox, float oy, float oz, Matrix4f dest) { + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + return dest + ._m00(sx * (m00 - ox * m03) + ox * m03) + ._m01(sy * (m01 - oy * m03) + oy * m03) + ._m02(sz * (m02 - oz * m03) + oz * m03) + ._m03(m03) + ._m10(sx * (m10 - ox * m13) + ox * m13) + ._m11(sy * (m11 - oy * m13) + oy * m13) + ._m12(sz * (m12 - oz * m13) + oz * m13) + ._m13(m13) + ._m20(sx * (m20 - ox * m23) + ox * m23) + ._m21(sy * (m21 - oy * m23) + oy * m23) + ._m22(sz * (m22 - oz * m23) + oz * m23) + ._m23(m23) + ._m30(sx * (m30 - ox * m33) + ox * m33) + ._m31(sy * (m31 - oy * m33) + oy * m33) + ._m32(sz * (m32 - oz * m33) + oz * m33) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | (one ? 0 : PROPERTY_ORTHONORMAL))); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix4f().translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz).mul(this, this) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4f scaleAroundLocal(float sx, float sy, float sz, float ox, float oy, float oz) { + return scaleAroundLocal(sx, sy, sz, ox, oy, oz, this); + } + + /** + * Pre-multiply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix4f().translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz).mul(this, this) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4f scaleAroundLocal(float factor, float ox, float oy, float oz) { + return scaleAroundLocal(factor, factor, factor, ox, oy, oz, this); + } + + public Matrix4f scaleAroundLocal(float factor, float ox, float oy, float oz, Matrix4f dest) { + return scaleAroundLocal(factor, factor, factor, ox, oy, oz, dest); + } + + public Matrix4f rotateX(float ang, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationX(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float x = m30, y = m31, z = m32; + return dest.rotationX(ang).setTranslation(x, y, z); + } + return rotateXInternal(ang, dest); + } + private Matrix4f rotateXInternal(float ang, Matrix4f dest) { + float sin = Math.sin(ang), cos = Math.cosFromSin(sin, ang); + float lm10 = m10, lm11 = m11, lm12 = m12, lm13 = m13, lm20 = m20, lm21 = m21, lm22 = m22, lm23 = m23; + return dest + ._m20(Math.fma(lm10, -sin, lm20 * cos)) + ._m21(Math.fma(lm11, -sin, lm21 * cos)) + ._m22(Math.fma(lm12, -sin, lm22 * cos)) + ._m23(Math.fma(lm13, -sin, lm23 * cos)) + ._m10(Math.fma(lm10, cos, lm20 * sin)) + ._m11(Math.fma(lm11, cos, lm21 * sin)) + ._m12(Math.fma(lm12, cos, lm22 * sin)) + ._m13(Math.fma(lm13, cos, lm23 * sin)) + ._m00(m00) + ._m01(m01) + ._m02(m02) + ._m03(m03) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4f rotateX(float ang) { + return rotateX(ang, this); + } + + public Matrix4f rotateY(float ang, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationY(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float x = m30, y = m31, z = m32; + return dest.rotationY(ang).setTranslation(x, y, z); + } + return rotateYInternal(ang, dest); + } + private Matrix4f rotateYInternal(float ang, Matrix4f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + // add temporaries for dependent values + float nm00 = Math.fma(m00, cos, m20 * -sin); + float nm01 = Math.fma(m01, cos, m21 * -sin); + float nm02 = Math.fma(m02, cos, m22 * -sin); + float nm03 = Math.fma(m03, cos, m23 * -sin); + // set non-dependent values directly + return dest + ._m20(Math.fma(m00, sin, m20 * cos)) + ._m21(Math.fma(m01, sin, m21 * cos)) + ._m22(Math.fma(m02, sin, m22 * cos)) + ._m23(Math.fma(m03, sin, m23 * cos)) + // set other values + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(m10) + ._m11(m11) + ._m12(m12) + ._m13(m13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4f rotateY(float ang) { + return rotateY(ang, this); + } + + public Matrix4f rotateZ(float ang, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationZ(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float x = m30, y = m31, z = m32; + return dest.rotationZ(ang).setTranslation(x, y, z); + } + return rotateZInternal(ang, dest); + } + private Matrix4f rotateZInternal(float ang, Matrix4f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + return rotateTowardsXY(sin, cos, dest); + } + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4f rotateZ(float ang) { + return rotateZ(ang, this); + } + + /** + * Apply rotation about the Z axis to align the local +X towards (dirX, dirY). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * The vector (dirX, dirY) must be a unit vector. + * + * @param dirX + * the x component of the normalized direction + * @param dirY + * the y component of the normalized direction + * @return this + */ + public Matrix4f rotateTowardsXY(float dirX, float dirY) { + return rotateTowardsXY(dirX, dirY, this); + } + + public Matrix4f rotateTowardsXY(float dirX, float dirY, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationTowardsXY(dirX, dirY); + float nm00 = Math.fma(m00, dirY, m10 * dirX); + float nm01 = Math.fma(m01, dirY, m11 * dirX); + float nm02 = Math.fma(m02, dirY, m12 * dirX); + float nm03 = Math.fma(m03, dirY, m13 * dirX); + return dest + ._m10(Math.fma(m00, -dirX, m10 * dirY)) + ._m11(Math.fma(m01, -dirX, m11 * dirY)) + ._m12(Math.fma(m02, -dirX, m12 * dirY)) + ._m13(Math.fma(m03, -dirX, m13 * dirY)) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation of angles.x radians about the X axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angles.x()).rotateY(angles.y()).rotateZ(angles.z()) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4f rotateXYZ(Vector3fc angles) { + return rotateXYZ(angles.x(), angles.y(), angles.z()); + } + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4f rotateXYZ(float angleX, float angleY, float angleZ) { + return rotateXYZ(angleX, angleY, angleZ, this); + } + + public Matrix4f rotateXYZ(float angleX, float angleY, float angleZ, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationXYZ(angleX, angleY, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float tx = m30, ty = m31, tz = m32; + return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz); + } else if ((properties & PROPERTY_AFFINE) != 0) + return dest.rotateAffineXYZ(angleX, angleY, angleZ); + return rotateXYZInternal(angleX, angleY, angleZ, dest); + } + private Matrix4f rotateXYZInternal(float angleX, float angleY, float angleZ, Matrix4f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinX = -sinX; + float m_sinY = -sinY; + float m_sinZ = -sinZ; + + // rotateX + float nm10 = Math.fma(m10, cosX, m20 * sinX); + float nm11 = Math.fma(m11, cosX, m21 * sinX); + float nm12 = Math.fma(m12, cosX, m22 * sinX); + float nm13 = Math.fma(m13, cosX, m23 * sinX); + float nm20 = Math.fma(m10, m_sinX, m20 * cosX); + float nm21 = Math.fma(m11, m_sinX, m21 * cosX); + float nm22 = Math.fma(m12, m_sinX, m22 * cosX); + float nm23 = Math.fma(m13, m_sinX, m23 * cosX); + // rotateY + float nm00 = Math.fma(m00, cosY, nm20 * m_sinY); + float nm01 = Math.fma(m01, cosY, nm21 * m_sinY); + float nm02 = Math.fma(m02, cosY, nm22 * m_sinY); + float nm03 = Math.fma(m03, cosY, nm23 * m_sinY); + return dest + ._m20(Math.fma(m00, sinY, nm20 * cosY)) + ._m21(Math.fma(m01, sinY, nm21 * cosY)) + ._m22(Math.fma(m02, sinY, nm22 * cosY)) + ._m23(Math.fma(m03, sinY, nm23 * cosY)) + // rotateZ + ._m00(Math.fma(nm00, cosZ, nm10 * sinZ)) + ._m01(Math.fma(nm01, cosZ, nm11 * sinZ)) + ._m02(Math.fma(nm02, cosZ, nm12 * sinZ)) + ._m03(Math.fma(nm03, cosZ, nm13 * sinZ)) + ._m10(Math.fma(nm00, m_sinZ, nm10 * cosZ)) + ._m11(Math.fma(nm01, m_sinZ, nm11 * cosZ)) + ._m12(Math.fma(nm02, m_sinZ, nm12 * cosZ)) + ._m13(Math.fma(nm03, m_sinZ, nm13 * cosZ)) + // copy last column from 'this' + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4f rotateAffineXYZ(float angleX, float angleY, float angleZ) { + return rotateAffineXYZ(angleX, angleY, angleZ, this); + } + + public Matrix4f rotateAffineXYZ(float angleX, float angleY, float angleZ, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationXYZ(angleX, angleY, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float tx = m30, ty = m31, tz = m32; + return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz); + } + return rotateAffineXYZInternal(angleX, angleY, angleZ, dest); + } + private Matrix4f rotateAffineXYZInternal(float angleX, float angleY, float angleZ, Matrix4f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinX = -sinX; + float m_sinY = -sinY; + float m_sinZ = -sinZ; + + // rotateX + float nm10 = Math.fma(m10, cosX, m20 * sinX); + float nm11 = Math.fma(m11, cosX, m21 * sinX); + float nm12 = Math.fma(m12, cosX, m22 * sinX); + float nm20 = Math.fma(m10, m_sinX, m20 * cosX); + float nm21 = Math.fma(m11, m_sinX, m21 * cosX); + float nm22 = Math.fma(m12, m_sinX, m22 * cosX); + // rotateY + float nm00 = Math.fma(m00, cosY, nm20 * m_sinY); + float nm01 = Math.fma(m01, cosY, nm21 * m_sinY); + float nm02 = Math.fma(m02, cosY, nm22 * m_sinY); + return dest + ._m20(Math.fma(m00, sinY, nm20 * cosY)) + ._m21(Math.fma(m01, sinY, nm21 * cosY)) + ._m22(Math.fma(m02, sinY, nm22 * cosY)) + ._m23(0.0f) + // rotateZ + ._m00(Math.fma(nm00, cosZ, nm10 * sinZ)) + ._m01(Math.fma(nm01, cosZ, nm11 * sinZ)) + ._m02(Math.fma(nm02, cosZ, nm12 * sinZ)) + ._m03(0.0f) + ._m10(Math.fma(nm00, m_sinZ, nm10 * cosZ)) + ._m11(Math.fma(nm01, m_sinZ, nm11 * cosZ)) + ._m12(Math.fma(nm02, m_sinZ, nm12 * cosZ)) + ._m13(0.0f) + // copy last column from 'this' + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation of angles.z radians about the Z axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.x radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angles.z).rotateY(angles.y).rotateX(angles.x) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4f rotateZYX(Vector3f angles) { + return rotateZYX(angles.z, angles.y, angles.x); + } + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4f rotateZYX(float angleZ, float angleY, float angleX) { + return rotateZYX(angleZ, angleY, angleX, this); + } + + public Matrix4f rotateZYX(float angleZ, float angleY, float angleX, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationZYX(angleZ, angleY, angleX); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float tx = m30, ty = m31, tz = m32; + return dest.rotationZYX(angleZ, angleY, angleX).setTranslation(tx, ty, tz); + } else if ((properties & PROPERTY_AFFINE) != 0) + return dest.rotateAffineZYX(angleZ, angleY, angleX); + return rotateZYXInternal(angleZ, angleY, angleX, dest); + } + private Matrix4f rotateZYXInternal(float angleZ, float angleY, float angleX, Matrix4f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinZ = -sinZ; + float m_sinY = -sinY; + float m_sinX = -sinX; + + // rotateZ + float nm00 = m00 * cosZ + m10 * sinZ; + float nm01 = m01 * cosZ + m11 * sinZ; + float nm02 = m02 * cosZ + m12 * sinZ; + float nm03 = m03 * cosZ + m13 * sinZ; + float nm10 = m00 * m_sinZ + m10 * cosZ; + float nm11 = m01 * m_sinZ + m11 * cosZ; + float nm12 = m02 * m_sinZ + m12 * cosZ; + float nm13 = m03 * m_sinZ + m13 * cosZ; + // rotateY + float nm20 = nm00 * sinY + m20 * cosY; + float nm21 = nm01 * sinY + m21 * cosY; + float nm22 = nm02 * sinY + m22 * cosY; + float nm23 = nm03 * sinY + m23 * cosY; + return dest + ._m00(nm00 * cosY + m20 * m_sinY) + ._m01(nm01 * cosY + m21 * m_sinY) + ._m02(nm02 * cosY + m22 * m_sinY) + ._m03(nm03 * cosY + m23 * m_sinY) + ._m10(nm10 * cosX + nm20 * sinX) + ._m11(nm11 * cosX + nm21 * sinX) + ._m12(nm12 * cosX + nm22 * sinX) + ._m13(nm13 * cosX + nm23 * sinX) + ._m20(nm10 * m_sinX + nm20 * cosX) + ._m21(nm11 * m_sinX + nm21 * cosX) + ._m22(nm12 * m_sinX + nm22 * cosX) + ._m23(nm13 * m_sinX + nm23 * cosX) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4f rotateAffineZYX(float angleZ, float angleY, float angleX) { + return rotateAffineZYX(angleZ, angleY, angleX, this); + } + + public Matrix4f rotateAffineZYX(float angleZ, float angleY, float angleX, Matrix4f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinZ = -sinZ; + float m_sinY = -sinY; + float m_sinX = -sinX; + + // rotateZ + float nm00 = m00 * cosZ + m10 * sinZ; + float nm01 = m01 * cosZ + m11 * sinZ; + float nm02 = m02 * cosZ + m12 * sinZ; + float nm10 = m00 * m_sinZ + m10 * cosZ; + float nm11 = m01 * m_sinZ + m11 * cosZ; + float nm12 = m02 * m_sinZ + m12 * cosZ; + // rotateY + float nm20 = nm00 * sinY + m20 * cosY; + float nm21 = nm01 * sinY + m21 * cosY; + float nm22 = nm02 * sinY + m22 * cosY; + return dest + ._m00(nm00 * cosY + m20 * m_sinY) + ._m01(nm01 * cosY + m21 * m_sinY) + ._m02(nm02 * cosY + m22 * m_sinY) + ._m03(0.0f) + ._m10(nm10 * cosX + nm20 * sinX) + ._m11(nm11 * cosX + nm21 * sinX) + ._m12(nm12 * cosX + nm22 * sinX) + ._m13(0.0f) + ._m20(nm10 * m_sinX + nm20 * cosX) + ._m21(nm11 * m_sinX + nm21 * cosX) + ._m22(nm12 * m_sinX + nm22 * cosX) + ._m23(0.0f) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation of angles.y radians about the Y axis, followed by a rotation of angles.x radians about the X axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4f rotateYXZ(Vector3f angles) { + return rotateYXZ(angles.y, angles.x, angles.z); + } + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4f rotateYXZ(float angleY, float angleX, float angleZ) { + return rotateYXZ(angleY, angleX, angleZ, this); + } + + public Matrix4f rotateYXZ(float angleY, float angleX, float angleZ, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationYXZ(angleY, angleX, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float tx = m30, ty = m31, tz = m32; + return dest.rotationYXZ(angleY, angleX, angleZ).setTranslation(tx, ty, tz); + } else if ((properties & PROPERTY_AFFINE) != 0) + return dest.rotateAffineYXZ(angleY, angleX, angleZ); + return rotateYXZInternal(angleY, angleX, angleZ, dest); + } + private Matrix4f rotateYXZInternal(float angleY, float angleX, float angleZ, Matrix4f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinY = -sinY; + float m_sinX = -sinX; + float m_sinZ = -sinZ; + + // rotateY + float nm20 = m00 * sinY + m20 * cosY; + float nm21 = m01 * sinY + m21 * cosY; + float nm22 = m02 * sinY + m22 * cosY; + float nm23 = m03 * sinY + m23 * cosY; + float nm00 = m00 * cosY + m20 * m_sinY; + float nm01 = m01 * cosY + m21 * m_sinY; + float nm02 = m02 * cosY + m22 * m_sinY; + float nm03 = m03 * cosY + m23 * m_sinY; + // rotateX + float nm10 = m10 * cosX + nm20 * sinX; + float nm11 = m11 * cosX + nm21 * sinX; + float nm12 = m12 * cosX + nm22 * sinX; + float nm13 = m13 * cosX + nm23 * sinX; + return dest + ._m20(m10 * m_sinX + nm20 * cosX) + ._m21(m11 * m_sinX + nm21 * cosX) + ._m22(m12 * m_sinX + nm22 * cosX) + ._m23(m13 * m_sinX + nm23 * cosX) + ._m00(nm00 * cosZ + nm10 * sinZ) + ._m01(nm01 * cosZ + nm11 * sinZ) + ._m02(nm02 * cosZ + nm12 * sinZ) + ._m03(nm03 * cosZ + nm13 * sinZ) + ._m10(nm00 * m_sinZ + nm10 * cosZ) + ._m11(nm01 * m_sinZ + nm11 * cosZ) + ._m12(nm02 * m_sinZ + nm12 * cosZ) + ._m13(nm03 * m_sinZ + nm13 * cosZ) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4f rotateAffineYXZ(float angleY, float angleX, float angleZ) { + return rotateAffineYXZ(angleY, angleX, angleZ, this); + } + + public Matrix4f rotateAffineYXZ(float angleY, float angleX, float angleZ, Matrix4f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinY = -sinY; + float m_sinX = -sinX; + float m_sinZ = -sinZ; + + // rotateY + float nm20 = m00 * sinY + m20 * cosY; + float nm21 = m01 * sinY + m21 * cosY; + float nm22 = m02 * sinY + m22 * cosY; + float nm00 = m00 * cosY + m20 * m_sinY; + float nm01 = m01 * cosY + m21 * m_sinY; + float nm02 = m02 * cosY + m22 * m_sinY; + // rotateX + float nm10 = m10 * cosX + nm20 * sinX; + float nm11 = m11 * cosX + nm21 * sinX; + float nm12 = m12 * cosX + nm22 * sinX; + return dest + ._m20(m10 * m_sinX + nm20 * cosX) + ._m21(m11 * m_sinX + nm21 * cosX) + ._m22(m12 * m_sinX + nm22 * cosX) + ._m23(0.0f) + ._m00(nm00 * cosZ + nm10 * sinZ) + ._m01(nm01 * cosZ + nm11 * sinZ) + ._m02(nm02 * cosZ + nm12 * sinZ) + ._m03(0.0f) + ._m10(nm00 * m_sinZ + nm10 * cosZ) + ._m11(nm01 * m_sinZ + nm11 * cosZ) + ._m12(nm02 * m_sinZ + nm12 * cosZ) + ._m13(0.0f) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotate(float ang, float x, float y, float z, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(ang, x, y, z); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(ang, x, y, z, dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return rotateAffine(ang, x, y, z, dest); + return rotateGeneric(ang, x, y, z, dest); + } + private Matrix4f rotateGeneric(float ang, float x, float y, float z, Matrix4f dest) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotateX(x * ang, dest); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotateY(y * ang, dest); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotateZ(z * ang, dest); + return rotateGenericInternal(ang, x, y, z, dest); + } + private Matrix4f rotateGenericInternal(float ang, float x, float y, float z, Matrix4f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float rm00 = xx * C + c; + float rm01 = xy * C + z * s; + float rm02 = xz * C - y * s; + float rm10 = xy * C - z * s; + float rm11 = yy * C + c; + float rm12 = yz * C + x * s; + float rm20 = xz * C + y * s; + float rm21 = yz * C - x * s; + float rm22 = zz * C + c; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + float nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + return dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4f rotate(float ang, float x, float y, float z) { + return rotate(ang, x, y, z, this); + } + + /** + * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateTranslation(float ang, float x, float y, float z, Matrix4f dest) { + float tx = m30, ty = m31, tz = m32; + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return dest.rotationX(x * ang).setTranslation(tx, ty, tz); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return dest.rotationY(y * ang).setTranslation(tx, ty, tz); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return dest.rotationZ(z * ang).setTranslation(tx, ty, tz); + return rotateTranslationInternal(ang, x, y, z, dest); + } + private Matrix4f rotateTranslationInternal(float ang, float x, float y, float z, Matrix4f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float rm00 = xx * C + c; + float rm01 = xy * C + z * s; + float rm02 = xz * C - y * s; + float rm10 = xy * C - z * s; + float rm11 = yy * C + c; + float rm12 = yz * C + x * s; + float rm20 = xz * C + y * s; + float rm21 = yz * C - x * s; + float rm22 = zz * C + c; + return dest + ._m20(rm20) + ._m21(rm21) + ._m22(rm22) + ._m00(rm00) + ._m01(rm01) + ._m02(rm02) + ._m03(0.0f) + ._m10(rm10) + ._m11(rm11) + ._m12(rm12) + ._m13(0.0f) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(1.0f) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateAffine(float ang, float x, float y, float z, Matrix4f dest) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotateX(x * ang, dest); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotateY(y * ang, dest); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotateZ(z * ang, dest); + return rotateAffineInternal(ang, x, y, z, dest); + } + private Matrix4f rotateAffineInternal(float ang, float x, float y, float z, Matrix4f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float rm00 = xx * C + c; + float rm01 = xy * C + z * s; + float rm02 = xz * C - y * s; + float rm10 = xy * C - z * s; + float rm11 = yy * C + c; + float rm12 = yz * C + x * s; + float rm20 = xz * C + y * s; + float rm21 = yz * C - x * s; + float rm22 = zz * C + c; + // add temporaries for dependent values + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + // set non-dependent values directly + return dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0f) + // set other values + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0f) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0f) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(1.0f) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4f rotateAffine(float ang, float x, float y, float z) { + return rotateAffine(ang, x, y, z, this); + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateLocal(float ang, float x, float y, float z, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(ang, x, y, z); + return rotateLocalGeneric(ang, x, y, z, dest); + } + private Matrix4f rotateLocalGeneric(float ang, float x, float y, float z, Matrix4f dest) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotateLocalX(x * ang, dest); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotateLocalY(y * ang, dest); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotateLocalZ(z * ang, dest); + return rotateLocalGenericInternal(ang, x, y, z, dest); + } + private Matrix4f rotateLocalGenericInternal(float ang, float x, float y, float z, Matrix4f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float lm00 = xx * C + c; + float lm01 = xy * C + z * s; + float lm02 = xz * C - y * s; + float lm10 = xy * C - z * s; + float lm11 = yy * C + c; + float lm12 = yz * C + x * s; + float lm20 = xz * C + y * s; + float lm21 = yz * C - x * s; + float lm22 = zz * C + c; + float nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + float nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + float nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + float nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + float nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + float nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + float nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + float nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + float nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + float nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + float nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + float nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(m03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(m13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4f rotateLocal(float ang, float x, float y, float z) { + return rotateLocal(ang, x, y, z, this); + } + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(float) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(float) + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateLocalX(float ang, Matrix4f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm02 = sin * m01 + cos * m02; + float nm12 = sin * m11 + cos * m12; + float nm22 = sin * m21 + cos * m22; + float nm32 = sin * m31 + cos * m32; + return dest + ._m00(m00) + ._m01(cos * m01 - sin * m02) + ._m02(nm02) + ._m03(m03) + ._m10(m10) + ._m11(cos * m11 - sin * m12) + ._m12(nm12) + ._m13(m13) + ._m20(m20) + ._m21(cos * m21 - sin * m22) + ._m22(nm22) + ._m23(m23) + ._m30(m30) + ._m31(cos * m31 - sin * m32) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(float) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(float) + * + * @param ang + * the angle in radians to rotate about the X axis + * @return this + */ + public Matrix4f rotateLocalX(float ang) { + return rotateLocalX(ang, this); + } + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateLocalY(float ang, Matrix4f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm02 = -sin * m00 + cos * m02; + float nm12 = -sin * m10 + cos * m12; + float nm22 = -sin * m20 + cos * m22; + float nm32 = -sin * m30 + cos * m32; + return dest + ._m00(cos * m00 + sin * m02) + ._m01(m01) + ._m02(nm02) + ._m03(m03) + ._m10(cos * m10 + sin * m12) + ._m11(m11) + ._m12(nm12) + ._m13(m13) + ._m20(cos * m20 + sin * m22) + ._m21(m21) + ._m22(nm22) + ._m23(m23) + ._m30(cos * m30 + sin * m32) + ._m31(m31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @return this + */ + public Matrix4f rotateLocalY(float ang) { + return rotateLocalY(ang, this); + } + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(float) rotationZ()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationZ(float) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateLocalZ(float ang, Matrix4f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm01 = sin * m00 + cos * m01; + float nm11 = sin * m10 + cos * m11; + float nm21 = sin * m20 + cos * m21; + float nm31 = sin * m30 + cos * m31; + return dest + ._m00(cos * m00 - sin * m01) + ._m01(nm01) + ._m02(m02) + ._m03(m03) + ._m10(cos * m10 - sin * m11) + ._m11(nm11) + ._m12(m12) + ._m13(m13) + ._m20(cos * m20 - sin * m21) + ._m21(nm21) + ._m22(m22) + ._m23(m23) + ._m30(cos * m30 - sin * m31) + ._m31(nm31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @return this + */ + public Matrix4f rotateLocalZ(float ang) { + return rotateLocalZ(ang, this); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4f translate(Vector3fc offset) { + return translate(offset.x(), offset.y(), offset.z()); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f translate(Vector3fc offset, Matrix4f dest) { + return translate(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(float, float, float)}. + * + * @see #translation(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f translate(float x, float y, float z, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.translation(x, y, z); + return translateGeneric(x, y, z, dest); + } + private Matrix4f translateGeneric(float x, float y, float z, Matrix4f dest) { + MemUtil.INSTANCE.copy(this, dest); + return dest + ._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30)))) + ._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31)))) + ._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32)))) + ._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)))) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY)); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(float, float, float)}. + * + * @see #translation(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4f translate(float x, float y, float z) { + if ((properties & PROPERTY_IDENTITY) != 0) + return translation(x, y, z); + return translateGeneric(x, y, z); + } + private Matrix4f translateGeneric(float x, float y, float z) { + return this + ._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30)))) + ._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31)))) + ._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32)))) + ._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)))) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY)); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4f translateLocal(Vector3fc offset) { + return translateLocal(offset.x(), offset.y(), offset.z()); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f translateLocal(Vector3fc offset, Matrix4f dest) { + return translateLocal(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(float, float, float)}. + * + * @see #translation(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f translateLocal(float x, float y, float z, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.translation(x, y, z); + return translateLocalGeneric(x, y, z, dest); + } + private Matrix4f translateLocalGeneric(float x, float y, float z, Matrix4f dest) { + float nm00 = m00 + x * m03; + float nm01 = m01 + y * m03; + float nm02 = m02 + z * m03; + float nm10 = m10 + x * m13; + float nm11 = m11 + y * m13; + float nm12 = m12 + z * m13; + float nm20 = m20 + x * m23; + float nm21 = m21 + y * m23; + float nm22 = m22 + z * m23; + float nm30 = m30 + x * m33; + float nm31 = m31 + y * m33; + float nm32 = m32 + z * m33; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(m03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(m13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY)); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(float, float, float)}. + * + * @see #translation(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4f translateLocal(float x, float y, float z) { + return translateLocal(x, y, z, this); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(m00); + out.writeFloat(m01); + out.writeFloat(m02); + out.writeFloat(m03); + out.writeFloat(m10); + out.writeFloat(m11); + out.writeFloat(m12); + out.writeFloat(m13); + out.writeFloat(m20); + out.writeFloat(m21); + out.writeFloat(m22); + out.writeFloat(m23); + out.writeFloat(m30); + out.writeFloat(m31); + out.writeFloat(m32); + out.writeFloat(m33); + } + + public void readExternal(ObjectInput in) throws IOException { + this._m00(in.readFloat()) + ._m01(in.readFloat()) + ._m02(in.readFloat()) + ._m03(in.readFloat()) + ._m10(in.readFloat()) + ._m11(in.readFloat()) + ._m12(in.readFloat()) + ._m13(in.readFloat()) + ._m20(in.readFloat()) + ._m21(in.readFloat()) + ._m22(in.readFloat()) + ._m23(in.readFloat()) + ._m30(in.readFloat()) + ._m31(in.readFloat()) + ._m32(in.readFloat()) + ._m33(in.readFloat()) + .determineProperties(); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(float, float, float, float, float, float, boolean) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f ortho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrtho(left, right, bottom, top, zNear, zFar, zZeroToOne); + return orthoGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f orthoGeneric(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + // calculate right matrix elements + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm22 = (zZeroToOne ? 1.0f : 2.0f) / (zNear - zFar); + float rm30 = (left + right) / (left - right); + float rm31 = (top + bottom) / (bottom - top); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20 * rm22) + ._m21(m21 * rm22) + ._m22(m22 * rm22) + ._m23(m23 * rm22) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(float, float, float, float, float, float) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f ortho(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4f dest) { + return ortho(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system using the given NDC z range to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(float, float, float, float, float, float, boolean) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f ortho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + return ortho(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(float, float, float, float, float, float) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4f ortho(float left, float right, float bottom, float top, float zNear, float zFar) { + return ortho(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(float, float, float, float, float, float, boolean) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne); + return orthoLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f orthoLHGeneric(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + // calculate right matrix elements + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm22 = (zZeroToOne ? 1.0f : 2.0f) / (zFar - zNear); + float rm30 = (left + right) / (left - right); + float rm31 = (top + bottom) / (bottom - top); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20 * rm22) + ._m21(m21 * rm22) + ._m22(m22 * rm22) + ._m23(m23 * rm22) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(float, float, float, float, float, float) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4f dest) { + return orthoLH(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(float, float, float, float, float, float, boolean) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + return orthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(float, float, float, float, float, float) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar) { + return orthoLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho(float, float, float, float, float, float, boolean) ortho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setOrtho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00(2.0f / (right - left)) + ._m11(2.0f / (top - bottom)) + ._m22((zZeroToOne ? 1.0f : 2.0f) / (zNear - zFar)) + ._m30((right + left) / (left - right)) + ._m31((top + bottom) / (bottom - top)) + ._m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)) + ._properties(PROPERTY_AFFINE); + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho(float, float, float, float, float, float) ortho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4f setOrtho(float left, float right, float bottom, float top, float zNear, float zFar) { + return setOrtho(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #orthoLH(float, float, float, float, float, float, boolean) orthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setOrthoLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00(2.0f / (right - left)) + ._m11(2.0f / (top - bottom)) + ._m22((zZeroToOne ? 1.0f : 2.0f) / (zFar - zNear)) + ._m30((right + left) / (left - right)) + ._m31((top + bottom) / (bottom - top)) + ._m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)) + ._properties(PROPERTY_AFFINE); + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #orthoLH(float, float, float, float, float, float) orthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4f setOrthoLH(float left, float right, float bottom, float top, float zNear, float zFar) { + return setOrthoLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, boolean, Matrix4f) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(float, float, float, float, boolean) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4f orthoSymmetric(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrthoSymmetric(width, height, zNear, zFar, zZeroToOne); + return orthoSymmetricGeneric(width, height, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f orthoSymmetricGeneric(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + // calculate right matrix elements + float rm00 = 2.0f / width; + float rm11 = 2.0f / height; + float rm22 = (zZeroToOne ? 1.0f : 2.0f) / (zNear - zFar); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m20 * rm32 + m30) + ._m31(m21 * rm32 + m31) + ._m32(m22 * rm32 + m32) + ._m33(m23 * rm32 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20 * rm22) + ._m21(m21 * rm22) + ._m22(m22 * rm22) + ._m23(m23 * rm22) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, Matrix4f) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(float, float, float, float) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f orthoSymmetric(float width, float height, float zNear, float zFar, Matrix4f dest) { + return orthoSymmetric(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, boolean) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(float, float, float, float, boolean) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f orthoSymmetric(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + return orthoSymmetric(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(float, float, float, float) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4f orthoSymmetric(float width, float height, float zNear, float zFar) { + return orthoSymmetric(width, height, zNear, zFar, false, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, boolean, Matrix4f) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(float, float, float, float, boolean) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4f orthoSymmetricLH(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrthoSymmetricLH(width, height, zNear, zFar, zZeroToOne); + return orthoSymmetricLHGeneric(width, height, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f orthoSymmetricLHGeneric(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + // calculate right matrix elements + float rm00 = 2.0f / width; + float rm11 = 2.0f / height; + float rm22 = (zZeroToOne ? 1.0f : 2.0f) / (zFar - zNear); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m20 * rm32 + m30) + ._m31(m21 * rm32 + m31) + ._m32(m22 * rm32 + m32) + ._m33(m23 * rm32 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20 * rm22) + ._m21(m21 * rm22) + ._m22(m22 * rm22) + ._m23(m23 * rm22) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, Matrix4f) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(float, float, float, float) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f orthoSymmetricLH(float width, float height, float zNear, float zFar, Matrix4f dest) { + return orthoSymmetricLH(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, boolean) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(float, float, float, float, boolean) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f orthoSymmetricLH(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + return orthoSymmetricLH(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(float, float, float, float) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4f orthoSymmetricLH(float width, float height, float zNear, float zFar) { + return orthoSymmetricLH(width, height, zNear, zFar, false, this); + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system using the given NDC z range. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float, boolean) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetric(float, float, float, float, boolean) orthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetric(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setOrthoSymmetric(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00(2.0f / width) + ._m11(2.0f / height) + ._m22((zZeroToOne ? 1.0f : 2.0f) / (zNear - zFar)) + ._m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)) + ._properties(PROPERTY_AFFINE); + return this; + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetric(float, float, float, float) orthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetric(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4f setOrthoSymmetric(float width, float height, float zNear, float zFar) { + return setOrthoSymmetric(width, height, zNear, zFar, false); + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system using the given NDC z range. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float, boolean) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetricLH(float, float, float, float, boolean) orthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetricLH(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setOrthoSymmetricLH(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00(2.0f / width) + ._m11(2.0f / height) + ._m22((zZeroToOne ? 1.0f : 2.0f) / (zFar - zNear)) + ._m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)) + ._properties(PROPERTY_AFFINE); + return this; + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * This method is equivalent to calling {@link #setOrthoLH(float, float, float, float, float, float) setOrthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetricLH(float, float, float, float) orthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetricLH(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4f setOrthoSymmetricLH(float width, float height, float zNear, float zFar) { + return setOrthoSymmetricLH(width, height, zNear, zFar, false); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix + * and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, Matrix4f) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2D(float, float, float, float) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float, Matrix4f) + * @see #setOrtho2D(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f ortho2D(float left, float right, float bottom, float top, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrtho2D(left, right, bottom, top); + return ortho2DGeneric(left, right, bottom, top, dest); + } + private Matrix4f ortho2DGeneric(float left, float right, float bottom, float top, Matrix4f dest) { + // calculate right matrix elements + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm30 = (right + left) / (left - right); + float rm31 = (top + bottom) / (bottom - top); + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(-m20) + ._m21(-m21) + ._m22(-m22) + ._m23(-m23) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2D(float, float, float, float) setOrtho2D()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float) + * @see #setOrtho2D(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4f ortho2D(float left, float right, float bottom, float top) { + return ortho2D(left, right, bottom, top, this); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, Matrix4f) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2DLH(float, float, float, float) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float, Matrix4f) + * @see #setOrtho2DLH(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f ortho2DLH(float left, float right, float bottom, float top, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setOrtho2DLH(left, right, bottom, top); + return ortho2DLHGeneric(left, right, bottom, top, dest); + } + private Matrix4f ortho2DLHGeneric(float left, float right, float bottom, float top, Matrix4f dest) { + // calculate right matrix elements + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm30 = (right + left) / (left - right); + float rm31 = (top + bottom) / (bottom - top); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest._m30(m00 * rm30 + m10 * rm31 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m33) + ._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2DLH(float, float, float, float) setOrtho2DLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float) + * @see #setOrtho2DLH(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4f ortho2DLH(float left, float right, float bottom, float top) { + return ortho2DLH(left, right, bottom, top, this); + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float) setOrtho()} with + * zNear=-1 and zFar=+1. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho2D(float, float, float, float) ortho2D()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float) + * @see #ortho2D(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4f setOrtho2D(float left, float right, float bottom, float top) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00(2.0f / (right - left)) + ._m11(2.0f / (top - bottom)) + ._m22(-1.0f) + ._m30((right + left) / (left - right)) + ._m31((top + bottom) / (bottom - top)) + ._properties(PROPERTY_AFFINE); + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float) setOrthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho2DLH(float, float, float, float) ortho2DLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float) + * @see #ortho2DLH(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4f setOrtho2DLH(float left, float right, float bottom, float top) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00(2.0f / (right - left)) + ._m11(2.0f / (top - bottom)) + ._m30((right + left) / (left - right)) + ._m31((top + bottom) / (bottom - top)) + ._properties(PROPERTY_AFFINE); + return this; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3fc, Vector3fc, Vector3fc) lookAt} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3fc, Vector3fc) setLookAlong()}. + * + * @see #lookAlong(float, float, float, float, float, float) + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4f lookAlong(Vector3fc dir, Vector3fc up) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3fc, Vector3fc, Vector3fc) lookAt} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3fc, Vector3fc) setLookAlong()}. + * + * @see #lookAlong(float, float, float, float, float, float) + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f lookAlong(Vector3fc dir, Vector3fc up, Matrix4f dest) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(float, float, float, float, float, float) setLookAlong()} + * + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * @see #setLookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAlong(dirX, dirY, dirZ, upX, upY, upZ); + return lookAlongGeneric(dirX, dirY, dirZ, upX, upY, upZ, dest); + } + + private Matrix4f lookAlongGeneric(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4f dest) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + // perform optimized matrix multiplication + // introduce temporaries for dependent results + float nm00 = m00 * leftX + m10 * upnX + m20 * dirX; + float nm01 = m01 * leftX + m11 * upnX + m21 * dirX; + float nm02 = m02 * leftX + m12 * upnX + m22 * dirX; + float nm03 = m03 * leftX + m13 * upnX + m23 * dirX; + float nm10 = m00 * leftY + m10 * upnY + m20 * dirY; + float nm11 = m01 * leftY + m11 * upnY + m21 * dirY; + float nm12 = m02 * leftY + m12 * upnY + m22 * dirY; + float nm13 = m03 * leftY + m13 * upnY + m23 * dirY; + return dest + ._m20(m00 * leftZ + m10 * upnZ + m20 * dirZ) + ._m21(m01 * leftZ + m11 * upnZ + m21 * dirZ) + ._m22(m02 * leftZ + m12 * upnZ + m22 * dirZ) + ._m23(m03 * leftZ + m13 * upnZ + m23 * dirZ) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(float, float, float, float, float, float) setLookAlong()} + * + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * @see #setLookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f lookAlong(float dirX, float dirY, float dirZ, + float upX, float upY, float upZ) { + return lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * This is equivalent to calling + * {@link #setLookAt(Vector3fc, Vector3fc, Vector3fc) setLookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(Vector3fc, Vector3fc)}. + * + * @see #setLookAlong(Vector3fc, Vector3fc) + * @see #lookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4f setLookAlong(Vector3fc dir, Vector3fc up) { + return setLookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * This is equivalent to calling + * {@link #setLookAt(float, float, float, float, float, float, float, float, float) + * setLookAt()} with eye = (0, 0, 0) and center = dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(float, float, float, float, float, float) lookAlong()} + * + * @see #setLookAlong(float, float, float, float, float, float) + * @see #lookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f setLookAlong(float dirX, float dirY, float dirZ, + float upX, float upY, float upZ) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + this._m00(leftX) + ._m01(dirY * leftZ - dirZ * leftY) + ._m02(dirX) + ._m03(0.0f) + ._m10(leftY) + ._m11(dirZ * leftX - dirX * leftZ) + ._m12(dirY) + ._m13(0.0f) + ._m20(leftZ) + ._m21(dirX * leftY - dirY * leftX) + ._m22(dirZ) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, that aligns + * -z with center - eye. + *

+ * In order to not make use of vectors to specify eye, center and up but use primitives, + * like in the GLU function, use {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt()} + * instead. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAt(Vector3fc, Vector3fc, Vector3fc) lookAt()}. + * + * @see #setLookAt(float, float, float, float, float, float, float, float, float) + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4f setLookAt(Vector3fc eye, Vector3fc center, Vector3fc up) { + return setLookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt}. + * + * @see #setLookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f setLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + return this + ._m00(leftX) + ._m01(upnX) + ._m02(dirX) + ._m03(0.0f) + ._m10(leftY) + ._m11(upnY) + ._m12(dirY) + ._m13(0.0f) + ._m20(leftZ) + ._m21(upnZ) + ._m22(dirZ) + ._m23(0.0f) + ._m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)) + ._m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)) + ._m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f lookAt(Vector3fc eye, Vector3fc center, Vector3fc up, Matrix4f dest) { + return lookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4f lookAt(Vector3fc eye, Vector3fc center, Vector3fc up) { + return lookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt()}. + * + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAt(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f lookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + else if ((properties & PROPERTY_PERSPECTIVE) != 0) + return lookAtPerspective(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + return lookAtGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + } + private Matrix4f lookAtGeneric(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4f dest) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + float rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + float rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + float rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + // introduce temporaries for dependent results + float nm00 = m00 * leftX + m10 * upnX + m20 * dirX; + float nm01 = m01 * leftX + m11 * upnX + m21 * dirX; + float nm02 = m02 * leftX + m12 * upnX + m22 * dirX; + float nm03 = m03 * leftX + m13 * upnX + m23 * dirX; + float nm10 = m00 * leftY + m10 * upnY + m20 * dirY; + float nm11 = m01 * leftY + m11 * upnY + m21 * dirY; + float nm12 = m02 * leftY + m12 * upnY + m22 * dirY; + float nm13 = m03 * leftY + m13 * upnY + m23 * dirY; + + // perform optimized matrix multiplication + // compute last column first, because others do not depend on it + return dest + ._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + ._m20(m00 * leftZ + m10 * upnZ + m20 * dirZ) + ._m21(m01 * leftZ + m11 * upnZ + m21 * dirZ) + ._m22(m02 * leftZ + m12 * upnZ + m22 * dirZ) + ._m23(m03 * leftZ + m13 * upnZ + m23 * dirZ) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * This method assumes this to be a perspective transformation, obtained via + * {@link #frustum(float, float, float, float, float, float) frustum()} or {@link #perspective(float, float, float, float) perspective()} or + * one of their overloads. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt()}. + * + * @see #setLookAt(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f lookAtPerspective(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4f dest) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + float rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + float rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + float rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + float nm10 = m00 * leftY; + float nm20 = m00 * leftZ; + float nm21 = m11 * upnZ; + float nm30 = m00 * rm30; + float nm31 = m11 * rm31; + float nm32 = m22 * rm32 + m32; + float nm33 = m23 * rm32; + return dest + ._m00(m00 * leftX) + ._m01(m11 * upnX) + ._m02(m22 * dirX) + ._m03(m23 * dirX) + ._m10(nm10) + ._m11(m11 * upnY) + ._m12(m22 * dirY) + ._m13(m23 * dirY) + ._m20(nm20) + ._m21(nm21) + ._m22(m22 * dirZ) + ._m23(m23 * dirZ) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt()}. + * + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAt(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f lookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + return lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, that aligns + * +z with center - eye. + *

+ * In order to not make use of vectors to specify eye, center and up but use primitives, + * like in the GLU function, use {@link #setLookAtLH(float, float, float, float, float, float, float, float, float) setLookAtLH()} + * instead. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAtLH(Vector3fc, Vector3fc, Vector3fc) lookAt()}. + * + * @see #setLookAtLH(float, float, float, float, float, float, float, float, float) + * @see #lookAtLH(Vector3fc, Vector3fc, Vector3fc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4f setLookAtLH(Vector3fc eye, Vector3fc center, Vector3fc up) { + return setLookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAtLH(float, float, float, float, float, float, float, float, float) lookAtLH}. + * + * @see #setLookAtLH(Vector3fc, Vector3fc, Vector3fc) + * @see #lookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f setLookAtLH(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + this._m00(leftX) + ._m01(upnX) + ._m02(dirX) + ._m03(0.0f) + ._m10(leftY) + ._m11(upnY) + ._m12(dirY) + ._m13(0.0f) + ._m20(leftZ) + ._m21(upnZ) + ._m22(dirZ) + ._m23(0.0f) + ._m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)) + ._m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)) + ._m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #lookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f lookAtLH(Vector3fc eye, Vector3fc center, Vector3fc up, Matrix4f dest) { + return lookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #lookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4f lookAtLH(Vector3fc eye, Vector3fc center, Vector3fc up) { + return lookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(float, float, float, float, float, float, float, float, float) setLookAtLH()}. + * + * @see #lookAtLH(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f lookAtLH(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + else if ((properties & PROPERTY_PERSPECTIVE) != 0) + return lookAtPerspectiveLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + return lookAtLHGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + } + private Matrix4f lookAtLHGeneric(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4f dest) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + float rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + float rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + float rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + // introduce temporaries for dependent results + float nm00 = m00 * leftX + m10 * upnX + m20 * dirX; + float nm01 = m01 * leftX + m11 * upnX + m21 * dirX; + float nm02 = m02 * leftX + m12 * upnX + m22 * dirX; + float nm03 = m03 * leftX + m13 * upnX + m23 * dirX; + float nm10 = m00 * leftY + m10 * upnY + m20 * dirY; + float nm11 = m01 * leftY + m11 * upnY + m21 * dirY; + float nm12 = m02 * leftY + m12 * upnY + m22 * dirY; + float nm13 = m03 * leftY + m13 * upnY + m23 * dirY; + + // perform optimized matrix multiplication + // compute last column first, because others do not depend on it + return dest + ._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33) + ._m20(m00 * leftZ + m10 * upnZ + m20 * dirZ) + ._m21(m01 * leftZ + m11 * upnZ + m21 * dirZ) + ._m22(m02 * leftZ + m12 * upnZ + m22 * dirZ) + ._m23(m03 * leftZ + m13 * upnZ + m23 * dirZ) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(float, float, float, float, float, float, float, float, float) setLookAtLH()}. + * + * @see #lookAtLH(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f lookAtLH(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + return lookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * This method assumes this to be a perspective transformation, obtained via + * {@link #frustumLH(float, float, float, float, float, float) frustumLH()} or {@link #perspectiveLH(float, float, float, float) perspectiveLH()} or + * one of their overloads. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(float, float, float, float, float, float, float, float, float) setLookAtLH()}. + * + * @see #setLookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f lookAtPerspectiveLH(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4f dest) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + float rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + float rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + float rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + + float nm00 = m00 * leftX; + float nm01 = m11 * upnX; + float nm02 = m22 * dirX; + float nm03 = m23 * dirX; + float nm10 = m00 * leftY; + float nm11 = m11 * upnY; + float nm12 = m22 * dirY; + float nm13 = m23 * dirY; + float nm20 = m00 * leftZ; + float nm21 = m11 * upnZ; + float nm22 = m22 * dirZ; + float nm23 = m23 * dirZ; + float nm30 = m00 * rm30; + float nm31 = m11 * rm31; + float nm32 = m22 * rm32 + m32; + float nm33 = m23 * rm32; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(nm33) + ._properties(0); + } + + /** + * This method is equivalent to calling: translate(w-1-2*x, h-1-2*y, 0).scale(w, h, 1) + *

+ * If M is this matrix and T the created transformation matrix, + * then the new matrix will be M * T. So when transforming a + * vector v with the new matrix by using M * T * v, the + * created transformation will be applied first! + * + * @param x + * the tile's x coordinate/index (should be in [0..w)) + * @param y + * the tile's y coordinate/index (should be in [0..h)) + * @param w + * the number of tiles along the x axis + * @param h + * the number of tiles along the y axis + * @return this + */ + public Matrix4f tile(int x, int y, int w, int h) { + return tile(x, y, w, h, this); + } + public Matrix4f tile(int x, int y, int w, int h, Matrix4f dest) { + float tx = w - 1 - (x<<1), ty = h - 1 - (y<<1); + return dest + ._m30(Math.fma(m00, tx, Math.fma(m10, ty, m30))) + ._m31(Math.fma(m01, tx, Math.fma(m11, ty, m31))) + ._m32(Math.fma(m02, tx, Math.fma(m12, ty, m32))) + ._m33(Math.fma(m03, tx, Math.fma(m13, ty, m33))) + ._m00(m00 * w) + ._m01(m01 * w) + ._m02(m02 * w) + ._m03(m03 * w) + ._m10(m10 * h) + ._m11(m11 * h) + ._m12(m12 * h) + ._m13(m13 * h) + ._m20(m20) + ._m21(m21) + ._m22(m22) + ._m23(m23) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspective(float, float, float, float, boolean) setPerspective}. + * + * @see #setPerspective(float, float, float, float, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4f perspective(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setPerspective(fovy, aspect, zNear, zFar, zZeroToOne); + return perspectiveGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f perspectiveGeneric(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + float h = Math.tan(fovy * 0.5f); + // calculate right matrix elements + float rm00 = 1.0f / (h * aspect); + float rm11 = 1.0f / h; + float rm22; + float rm32; + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + rm22 = e - 1.0f; + rm32 = (e - (zZeroToOne ? 1.0f : 2.0f)) * zNear; + } else if (nearInf) { + float e = 1E-6f; + rm22 = (zZeroToOne ? 0.0f : 1.0f) - e; + rm32 = ((zZeroToOne ? 1.0f : 2.0f) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + float nm20 = m20 * rm22 - m30; + float nm21 = m21 * rm22 - m31; + float nm22 = m22 * rm22 - m32; + float nm23 = m23 * rm22 - m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspective(float, float, float, float) setPerspective}. + * + * @see #setPerspective(float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f perspective(float fovy, float aspect, float zNear, float zFar, Matrix4f dest) { + return perspective(fovy, aspect, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspective(float, float, float, float, boolean) setPerspective}. + * + * @see #setPerspective(float, float, float, float, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f perspective(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne) { + return perspective(fovy, aspect, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspective(float, float, float, float) setPerspective}. + * + * @see #setPerspective(float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f perspective(float fovy, float aspect, float zNear, float zFar) { + return perspective(fovy, aspect, zNear, zFar, this); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveRect(float, float, float, float, boolean) setPerspectiveRect}. + * + * @see #setPerspectiveRect(float, float, float, float, boolean) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4f perspectiveRect(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setPerspectiveRect(width, height, zNear, zFar, zZeroToOne); + return perspectiveRectGeneric(width, height, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f perspectiveRectGeneric(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + float rm00 = (zNear + zNear) / width; + float rm11 = (zNear + zNear) / height; + float rm22, rm32; + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + rm22 = e - 1.0f; + rm32 = (e - (zZeroToOne ? 1.0f : 2.0f)) * zNear; + } else if (nearInf) { + float e = 1E-6f; + rm22 = (zZeroToOne ? 0.0f : 1.0f) - e; + rm32 = ((zZeroToOne ? 1.0f : 2.0f) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + float nm20 = m20 * rm22 - m30; + float nm21 = m21 * rm22 - m31; + float nm22 = m22 * rm22 - m32; + float nm23 = m23 * rm22 - m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveRect(float, float, float, float) setPerspectiveRect}. + * + * @see #setPerspectiveRect(float, float, float, float) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f perspectiveRect(float width, float height, float zNear, float zFar, Matrix4f dest) { + return perspectiveRect(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveRect(float, float, float, float, boolean) setPerspectiveRect}. + * + * @see #setPerspectiveRect(float, float, float, float, boolean) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f perspectiveRect(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + return perspectiveRect(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveRect(float, float, float, float) setPerspectiveRect}. + * + * @see #setPerspectiveRect(float, float, float, float) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f perspectiveRect(float width, float height, float zNear, float zFar) { + return perspectiveRect(width, height, zNear, zFar, this); + } + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveOffCenter(float, float, float, float, float, float, boolean) setPerspectiveOffCenter}. + * + * @see #setPerspectiveOffCenter(float, float, float, float, float, float, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4f perspectiveOffCenter(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne); + return perspectiveOffCenterGeneric(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f perspectiveOffCenterGeneric(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + float h = Math.tan(fovy * 0.5f); + // calculate right matrix elements + float xScale = 1.0f / (h * aspect); + float yScale = 1.0f / h; + float offX = Math.tan(offAngleX), offY = Math.tan(offAngleY); + float rm20 = offX * xScale; + float rm21 = offY * yScale; + float rm22; + float rm32; + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + rm22 = e - 1.0f; + rm32 = (e - (zZeroToOne ? 1.0f : 2.0f)) * zNear; + } else if (nearInf) { + float e = 1E-6f; + rm22 = (zZeroToOne ? 0.0f : 1.0f) - e; + rm32 = ((zZeroToOne ? 1.0f : 2.0f) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + float nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30; + float nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31; + float nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32; + float nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33; + dest._m00(m00 * xScale) + ._m01(m01 * xScale) + ._m02(m02 * xScale) + ._m03(m03 * xScale) + ._m10(m10 * yScale) + ._m11(m11 * yScale) + ._m12(m12 * yScale) + ._m13(m13 * yScale) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION + | PROPERTY_ORTHONORMAL | (rm20 == 0.0f && rm21 == 0.0f ? 0 : PROPERTY_PERSPECTIVE))); + return dest; + } + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveOffCenter(float, float, float, float, float, float) setPerspectiveOffCenter}. + * + * @see #setPerspectiveOffCenter(float, float, float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f perspectiveOffCenter(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar, Matrix4f dest) { + return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false, dest); + } + + /** + * Apply an asymmetric off-center perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveOffCenter(float, float, float, float, float, float, boolean) setPerspectiveOffCenter}. + * + * @see #setPerspectiveOffCenter(float, float, float, float, float, float, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f perspectiveOffCenter(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar, boolean zZeroToOne) { + return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveOffCenter(float, float, float, float, float, float) setPerspectiveOffCenter}. + * + * @see #setPerspectiveOffCenter(float, float, float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f perspectiveOffCenter(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar) { + return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, this); + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspective(float, float, float, float, boolean) perspective()}. + * + * @see #perspective(float, float, float, float, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setPerspective(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne) { + MemUtil.INSTANCE.zero(this); + float h = Math.tan(fovy * 0.5f); + this._m00(1.0f / (h * aspect)) + ._m11(1.0f / h); + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + this._m22(e - 1.0f) + ._m32((e - (zZeroToOne ? 1.0f : 2.0f)) * zNear); + } else if (nearInf) { + float e = 1E-6f; + this._m22((zZeroToOne ? 0.0f : 1.0f) - e) + ._m32(((zZeroToOne ? 1.0f : 2.0f) - e) * zFar); + } else { + this._m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)) + ._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + return this + ._m23(-1.0f) + ._properties(PROPERTY_PERSPECTIVE); + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspective(float, float, float, float) perspective()}. + * + * @see #perspective(float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f setPerspective(float fovy, float aspect, float zNear, float zFar) { + return setPerspective(fovy, aspect, zNear, zFar, false); + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveRect(float, float, float, float, boolean) perspectiveRect()}. + * + * @see #perspectiveRect(float, float, float, float, boolean) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setPerspectiveRect(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + MemUtil.INSTANCE.zero(this); + this._m00((zNear + zNear) / width) + ._m11((zNear + zNear) / height); + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + this._m22(e - 1.0f) + ._m32((e - (zZeroToOne ? 1.0f : 2.0f)) * zNear); + } else if (nearInf) { + float e = 1E-6f; + this._m22((zZeroToOne ? 0.0f : 1.0f) - e) + ._m32(((zZeroToOne ? 1.0f : 2.0f) - e) * zFar); + } else { + this._m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)) + ._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + this._m23(-1.0f) + ._properties(PROPERTY_PERSPECTIVE); + return this; + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveRect(float, float, float, float) perspectiveRect()}. + * + * @see #perspectiveRect(float, float, float, float) + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f setPerspectiveRect(float width, float height, float zNear, float zFar) { + return setPerspectiveRect(width, height, zNear, zFar, false); + } + + /** + * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed + * coordinate system using OpenGL's NDC z range of [-1..+1]. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveOffCenter(float, float, float, float, float, float) perspectiveOffCenter()}. + * + * @see #perspectiveOffCenter(float, float, float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f setPerspectiveOffCenter(float fovy, float offAngleX, float offAngleY, + float aspect, float zNear, float zFar) { + return setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false); + } + /** + * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed + * coordinate system using the given NDC z range. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveOffCenter(float, float, float, float, float, float) perspectiveOffCenter()}. + * + * @see #perspectiveOffCenter(float, float, float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setPerspectiveOffCenter(float fovy, float offAngleX, float offAngleY, + float aspect, float zNear, float zFar, boolean zZeroToOne) { + MemUtil.INSTANCE.zero(this); + float h = Math.tan(fovy * 0.5f); + float xScale = 1.0f / (h * aspect), yScale = 1.0f / h; + float offX = Math.tan(offAngleX), offY = Math.tan(offAngleY); + this._m00(xScale) + ._m11(yScale) + ._m20(offX * xScale) + ._m21(offY * yScale); + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + this._m22(e - 1.0f) + ._m32((e - (zZeroToOne ? 1.0f : 2.0f)) * zNear); + } else if (nearInf) { + float e = 1E-6f; + this._m22((zZeroToOne ? 0.0f : 1.0f) - e) + ._m32(((zZeroToOne ? 1.0f : 2.0f) - e) * zFar); + } else { + this._m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)) + ._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + this._m23(-1.0f) + ._properties(offAngleX == 0.0f && offAngleY == 0.0f ? PROPERTY_PERSPECTIVE : 0); + return this; + } + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveLH(float, float, float, float, boolean) setPerspectiveLH}. + * + * @see #setPerspectiveLH(float, float, float, float, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f perspectiveLH(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setPerspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne); + return perspectiveLHGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f perspectiveLHGeneric(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + float h = Math.tan(fovy * 0.5f); + // calculate right matrix elements + float rm00 = 1.0f / (h * aspect); + float rm11 = 1.0f / h; + float rm22; + float rm32; + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + rm22 = 1.0f - e; + rm32 = (e - (zZeroToOne ? 1.0f : 2.0f)) * zNear; + } else if (nearInf) { + float e = 1E-6f; + rm22 = (zZeroToOne ? 0.0f : 1.0f) - e; + rm32 = ((zZeroToOne ? 1.0f : 2.0f) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + float nm20 = m20 * rm22 + m30; + float nm21 = m21 * rm22 + m31; + float nm22 = m22 * rm22 + m32; + float nm23 = m23 * rm22 + m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveLH(float, float, float, float, boolean) setPerspectiveLH}. + * + * @see #setPerspectiveLH(float, float, float, float, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f perspectiveLH(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne) { + return perspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveLH(float, float, float, float) setPerspectiveLH}. + * + * @see #setPerspectiveLH(float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f perspectiveLH(float fovy, float aspect, float zNear, float zFar, Matrix4f dest) { + return perspectiveLH(fovy, aspect, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setPerspectiveLH(float, float, float, float) setPerspectiveLH}. + * + * @see #setPerspectiveLH(float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f perspectiveLH(float fovy, float aspect, float zNear, float zFar) { + return perspectiveLH(fovy, aspect, zNear, zFar, this); + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range of [-1..+1]. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveLH(float, float, float, float, boolean) perspectiveLH()}. + * + * @see #perspectiveLH(float, float, float, float, boolean) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setPerspectiveLH(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne) { + MemUtil.INSTANCE.zero(this); + float h = Math.tan(fovy * 0.5f); + this._m00(1.0f / (h * aspect)) + ._m11(1.0f / h); + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + this._m22(1.0f - e) + ._m32((e - (zZeroToOne ? 1.0f : 2.0f)) * zNear); + } else if (nearInf) { + float e = 1E-6f; + this._m22((zZeroToOne ? 0.0f : 1.0f) - e) + ._m32(((zZeroToOne ? 1.0f : 2.0f) - e) * zFar); + } else { + this._m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)) + ._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + this._m23(1.0f) + ._properties(PROPERTY_PERSPECTIVE); + return this; + } + + /** + * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective projection transformation to an existing transformation, + * use {@link #perspectiveLH(float, float, float, float) perspectiveLH()}. + * + * @see #perspectiveLH(float, float, float, float) + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f setPerspectiveLH(float fovy, float aspect, float zNear, float zFar) { + return setPerspectiveLH(fovy, aspect, zNear, zFar, false); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustum(float, float, float, float, float, float, boolean) setFrustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustum(float, float, float, float, float, float, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f frustum(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setFrustum(left, right, bottom, top, zNear, zFar, zZeroToOne); + return frustumGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f frustumGeneric(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + // calculate right matrix elements + float rm00 = (zNear + zNear) / (right - left); + float rm11 = (zNear + zNear) / (top - bottom); + float rm20 = (right + left) / (right - left); + float rm21 = (top + bottom) / (top - bottom); + float rm22; + float rm32; + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + rm22 = e - 1.0f; + rm32 = (e - (zZeroToOne ? 1.0f : 2.0f)) * zNear; + } else if (nearInf) { + float e = 1E-6f; + rm22 = (zZeroToOne ? 0.0f : 1.0f) - e; + rm32 = ((zZeroToOne ? 1.0f : 2.0f) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + float nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30; + float nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31; + float nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32; + float nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(0); + return dest; + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustum(float, float, float, float, float, float) setFrustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustum(float, float, float, float, float, float) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f frustum(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4f dest) { + return frustum(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustum(float, float, float, float, float, float, boolean) setFrustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustum(float, float, float, float, float, float, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f frustum(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + return frustum(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustum(float, float, float, float, float, float) setFrustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustum(float, float, float, float, float, float) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f frustum(float left, float right, float bottom, float top, float zNear, float zFar) { + return frustum(left, right, bottom, top, zNear, zFar, this); + } + + /** + * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the perspective frustum transformation to an existing transformation, + * use {@link #frustum(float, float, float, float, float, float, boolean) frustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #frustum(float, float, float, float, float, float, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setFrustum(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00((zNear + zNear) / (right - left)) + ._m11((zNear + zNear) / (top - bottom)) + ._m20((right + left) / (right - left)) + ._m21((top + bottom) / (top - bottom)); + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + this._m22(e - 1.0f) + ._m32((e - (zZeroToOne ? 1.0f : 2.0f)) * zNear); + } else if (nearInf) { + float e = 1E-6f; + this._m22((zZeroToOne ? 0.0f : 1.0f) - e) + ._m32(((zZeroToOne ? 1.0f : 2.0f) - e) * zFar); + } else { + this._m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)) + ._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + this._m23(-1.0f) + ._m33(0.0f) + ._properties(this.m20 == 0.0f && this.m21 == 0.0f ? PROPERTY_PERSPECTIVE : 0); + return this; + } + + /** + * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective frustum transformation to an existing transformation, + * use {@link #frustum(float, float, float, float, float, float) frustum()}. + *

+ * Reference: http://www.songho.ca + * + * @see #frustum(float, float, float, float, float, float) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f setFrustum(float left, float right, float bottom, float top, float zNear, float zFar) { + return setFrustum(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustumLH(float, float, float, float, float, float, boolean) setFrustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustumLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f frustumLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setFrustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne); + return frustumLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest); + } + private Matrix4f frustumLHGeneric(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest) { + // calculate right matrix elements + float rm00 = (zNear + zNear) / (right - left); + float rm11 = (zNear + zNear) / (top - bottom); + float rm20 = (right + left) / (right - left); + float rm21 = (top + bottom) / (top - bottom); + float rm22; + float rm32; + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + rm22 = 1.0f - e; + rm32 = (e - (zZeroToOne ? 1.0f : 2.0f)) * zNear; + } else if (nearInf) { + float e = 1E-6f; + rm22 = (zZeroToOne ? 0.0f : 1.0f) - e; + rm32 = ((zZeroToOne ? 1.0f : 2.0f) - e) * zFar; + } else { + rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear); + rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar); + } + // perform optimized matrix multiplication + float nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30; + float nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31; + float nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32; + float nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33; + dest._m00(m00 * rm00) + ._m01(m01 * rm00) + ._m02(m02 * rm00) + ._m03(m03 * rm00) + ._m10(m10 * rm11) + ._m11(m11 * rm11) + ._m12(m12 * rm11) + ._m13(m13 * rm11) + ._m30(m20 * rm32) + ._m31(m21 * rm32) + ._m32(m22 * rm32) + ._m33(m23 * rm32) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(0); + return dest; + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustumLH(float, float, float, float, float, float, boolean) setFrustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustumLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f frustumLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + return frustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustumLH(float, float, float, float, float, float) setFrustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustumLH(float, float, float, float, float, float) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f frustumLH(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4f dest) { + return frustumLH(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * In order to set the matrix to a perspective frustum transformation without post-multiplying, + * use {@link #setFrustumLH(float, float, float, float, float, float) setFrustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setFrustumLH(float, float, float, float, float, float) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f frustumLH(float left, float right, float bottom, float top, float zNear, float zFar) { + return frustumLH(left, right, bottom, top, zNear, zFar, this); + } + + /** + * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective frustum transformation to an existing transformation, + * use {@link #frustumLH(float, float, float, float, float, float, boolean) frustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #frustumLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4f setFrustumLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00((zNear + zNear) / (right - left)) + ._m11((zNear + zNear) / (top - bottom)) + ._m20((right + left) / (right - left)) + ._m21((top + bottom) / (top - bottom)); + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if (farInf) { + // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf) + float e = 1E-6f; + this._m22(1.0f - e) + ._m32((e - (zZeroToOne ? 1.0f : 2.0f)) * zNear); + } else if (nearInf) { + float e = 1E-6f; + this._m22((zZeroToOne ? 0.0f : 1.0f) - e) + ._m32(((zZeroToOne ? 1.0f : 2.0f) - e) * zFar); + } else { + this._m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)) + ._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar)); + } + return this + ._m23(1.0f) + ._m33(0.0f) + ._properties(0); + } + + /** + * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the perspective frustum transformation to an existing transformation, + * use {@link #frustumLH(float, float, float, float, float, float) frustumLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #frustumLH(float, float, float, float, float, float) + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + public Matrix4f setFrustumLH(float left, float right, float bottom, float top, float zNear, float zFar) { + return setFrustumLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to represent a perspective projection equivalent to the given intrinsic camera calibration parameters. + * The resulting matrix will be suited for a right-handed coordinate system using OpenGL's NDC z range of [-1..+1]. + *

+ * See: https://en.wikipedia.org/ + *

+ * Reference: http://ksimek.github.io/ + * + * @param alphaX + * specifies the focal length and scale along the X axis + * @param alphaY + * specifies the focal length and scale along the Y axis + * @param gamma + * the skew coefficient between the X and Y axis (may be 0) + * @param u0 + * the X coordinate of the principal point in image/sensor units + * @param v0 + * the Y coordinate of the principal point in image/sensor units + * @param imgWidth + * the width of the sensor/image image/sensor units + * @param imgHeight + * the height of the sensor/image image/sensor units + * @param near + * the distance to the near plane + * @param far + * the distance to the far plane + * @return this + */ + public Matrix4f setFromIntrinsic(float alphaX, float alphaY, float gamma, float u0, float v0, int imgWidth, int imgHeight, float near, float far) { + float l00 = 2.0f / imgWidth; + float l11 = 2.0f / imgHeight; + float l22 = 2.0f / (near - far); + this.m00 = l00 * alphaX; + this.m01 = 0.0f; + this.m02 = 0.0f; + this.m03 = 0.0f; + this.m10 = l00 * gamma; + this.m11 = l11 * alphaY; + this.m12 = 0.0f; + this.m13 = 0.0f; + this.m20 = l00 * u0 - 1.0f; + this.m21 = l11 * v0 - 1.0f; + this.m22 = l22 * -(near + far) + (far + near) / (near - far); + this.m23 = -1.0f; + this.m30 = 0.0f; + this.m31 = 0.0f; + this.m32 = l22 * -near * far; + this.m33 = 0.0f; + this.properties = PROPERTY_PERSPECTIVE; + return this; + } + + /** + * Apply the rotation transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotate(Quaternionfc quat, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(quat); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(quat, dest); + else if ((properties & PROPERTY_AFFINE) != 0) + return rotateAffine(quat, dest); + return rotateGeneric(quat, dest); + } + private Matrix4f rotateGeneric(Quaternionfc quat, Matrix4f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = -dzw + dxy; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + float nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + return dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply the rotation transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4f rotate(Quaternionfc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation transformation of the given {@link Quaternionfc} to this {@link #isAffine() affine} matrix and store + * the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateAffine(Quaternionfc quat, Matrix4f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = -dzw + dxy; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + return dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0f) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0f) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0f) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply the rotation transformation of the given {@link Quaternionfc} to this matrix. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4f rotateAffine(Quaternionfc quat) { + return rotateAffine(quat, this); + } + + /** + * Apply the rotation transformation of the given {@link Quaternionfc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateTranslation(Quaternionfc quat, Matrix4f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = -dzw + dxy; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + return dest + ._m20(rm20) + ._m21(rm21) + ._m22(rm22) + ._m23(0.0f) + ._m00(rm00) + ._m01(rm01) + ._m02(rm02) + ._m03(0.0f) + ._m10(rm10) + ._m11(rm11) + ._m12(rm12) + ._m13(0.0f) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Apply the rotation transformation of the given {@link Quaternionfc} to this matrix while using (ox, oy, oz) as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4f rotateAround(Quaternionfc quat, float ox, float oy, float oz) { + return rotateAround(quat, ox, oy, oz, this); + } + + public Matrix4f rotateAroundAffine(Quaternionfc quat, float ox, float oy, float oz, Matrix4f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = -dzw + dxy; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float tm30 = m00 * ox + m10 * oy + m20 * oz + m30; + float tm31 = m01 * ox + m11 * oy + m21 * oz + m31; + float tm32 = m02 * ox + m12 * oy + m22 * oz + m32; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0f) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0f) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0f) + ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30) + ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31) + ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32) + ._m33(1.0f) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + public Matrix4f rotateAround(Quaternionfc quat, float ox, float oy, float oz, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return rotationAround(quat, ox, oy, oz); + else if ((properties & PROPERTY_AFFINE) != 0) + return rotateAroundAffine(quat, ox, oy, oz, dest); + return rotateAroundGeneric(quat, ox, oy, oz, dest); + } + private Matrix4f rotateAroundGeneric(Quaternionfc quat, float ox, float oy, float oz, Matrix4f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = -dzw + dxy; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float tm30 = m00 * ox + m10 * oy + m20 * oz + m30; + float tm31 = m01 * ox + m11 * oy + m21 * oz + m31; + float tm32 = m02 * ox + m12 * oy + m22 * oz + m32; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + float nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30) + ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31) + ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Set this matrix to a transformation composed of a rotation of the specified {@link Quaternionfc} while using (ox, oy, oz) as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4f rotationAround(Quaternionfc quat, float ox, float oy, float oz) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + this._m20(dyw + dxz) + ._m21(dyz - dxw) + ._m22(z2 - y2 - x2 + w2) + ._m23(0.0f) + ._m00(w2 + x2 - z2 - y2) + ._m01(dxy + dzw) + ._m02(dxz - dyw) + ._m03(0.0f) + ._m10(-dzw + dxy) + ._m11(y2 - z2 + w2 - x2) + ._m12(dyz + dxw) + ._m13(0.0f) + ._m30(-m00 * ox - m10 * oy - m20 * oz + ox) + ._m31(-m01 * ox - m11 * oy - m21 * oz + oy) + ._m32(-m02 * ox - m12 * oy - m22 * oz + oz) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Pre-multiply the rotation transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateLocal(Quaternionfc quat, Matrix4f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float lm00 = w2 + x2 - z2 - y2; + float lm01 = dxy + dzw; + float lm02 = dxz - dyw; + float lm10 = -dzw + dxy; + float lm11 = y2 - z2 + w2 - x2; + float lm12 = dyz + dxw; + float lm20 = dyw + dxz; + float lm21 = dyz - dxw; + float lm22 = z2 - y2 - x2 + w2; + float nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + float nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + float nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + float nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + float nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + float nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + float nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + float nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + float nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + float nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + float nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + float nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(m03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(m13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(m23) + ._m30(nm30) + ._m31(nm31) + ._m32(nm32) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + } + + /** + * Pre-multiply the rotation transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4f rotateLocal(Quaternionfc quat) { + return rotateLocal(quat, this); + } + + public Matrix4f rotateAroundLocal(Quaternionfc quat, float ox, float oy, float oz, Matrix4f dest) { + float w2 = quat.w() * quat.w(); + float x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(); + float z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(); + float xy = quat.x() * quat.y(); + float xz = quat.x() * quat.z(); + float yw = quat.y() * quat.w(); + float yz = quat.y() * quat.z(); + float xw = quat.x() * quat.w(); + float lm00 = w2 + x2 - z2 - y2; + float lm01 = xy + zw + zw + xy; + float lm02 = xz - yw + xz - yw; + float lm10 = -zw + xy - zw + xy; + float lm11 = y2 - z2 + w2 - x2; + float lm12 = yz + yz + xw + xw; + float lm20 = yw + xz + xz + yw; + float lm21 = yz + yz - xw - xw; + float lm22 = z2 - y2 - x2 + w2; + float tm00 = m00 - ox * m03; + float tm01 = m01 - oy * m03; + float tm02 = m02 - oz * m03; + float tm10 = m10 - ox * m13; + float tm11 = m11 - oy * m13; + float tm12 = m12 - oz * m13; + float tm20 = m20 - ox * m23; + float tm21 = m21 - oy * m23; + float tm22 = m22 - oz * m23; + float tm30 = m30 - ox * m33; + float tm31 = m31 - oy * m33; + float tm32 = m32 - oz * m33; + dest._m00(lm00 * tm00 + lm10 * tm01 + lm20 * tm02 + ox * m03) + ._m01(lm01 * tm00 + lm11 * tm01 + lm21 * tm02 + oy * m03) + ._m02(lm02 * tm00 + lm12 * tm01 + lm22 * tm02 + oz * m03) + ._m03(m03) + ._m10(lm00 * tm10 + lm10 * tm11 + lm20 * tm12 + ox * m13) + ._m11(lm01 * tm10 + lm11 * tm11 + lm21 * tm12 + oy * m13) + ._m12(lm02 * tm10 + lm12 * tm11 + lm22 * tm12 + oz * m13) + ._m13(m13) + ._m20(lm00 * tm20 + lm10 * tm21 + lm20 * tm22 + ox * m23) + ._m21(lm01 * tm20 + lm11 * tm21 + lm21 * tm22 + oy * m23) + ._m22(lm02 * tm20 + lm12 * tm21 + lm22 * tm22 + oz * m23) + ._m23(m23) + ._m30(lm00 * tm30 + lm10 * tm31 + lm20 * tm32 + ox * m33) + ._m31(lm01 * tm30 + lm11 * tm31 + lm21 * tm32 + oy * m33) + ._m32(lm02 * tm30 + lm12 * tm31 + lm22 * tm32 + oz * m33) + ._m33(m33) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply the rotation transformation of the given {@link Quaternionfc} to this matrix while using (ox, oy, oz) + * as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * This method is equivalent to calling: translateLocal(-ox, -oy, -oz).rotateLocal(quat).translateLocal(ox, oy, oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4f rotateAroundLocal(Quaternionfc quat, float ox, float oy, float oz) { + return rotateAroundLocal(quat, ox, oy, oz, this); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f}, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix4f rotate(AxisAngle4f axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotate(AxisAngle4f axisAngle, Matrix4f dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis-angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(float, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix4f rotate(float angle, Vector3fc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis-angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(float, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotate(float angle, Vector3fc axis, Matrix4f dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + public Vector4f unproject(float winX, float winY, float winZ, int[] viewport, Vector4f dest) { + float a = m00 * m11 - m01 * m10; + float b = m00 * m12 - m02 * m10; + float c = m00 * m13 - m03 * m10; + float d = m01 * m12 - m02 * m11; + float e = m01 * m13 - m03 * m11; + float f = m02 * m13 - m03 * m12; + float g = m20 * m31 - m21 * m30; + float h = m20 * m32 - m22 * m30; + float i = m20 * m33 - m23 * m30; + float j = m21 * m32 - m22 * m31; + float k = m21 * m33 - m23 * m31; + float l = m22 * m33 - m23 * m32; + float det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0f / det; + float im00 = ( m11 * l - m12 * k + m13 * j) * det; + float im01 = (-m01 * l + m02 * k - m03 * j) * det; + float im02 = ( m31 * f - m32 * e + m33 * d) * det; + float im03 = (-m21 * f + m22 * e - m23 * d) * det; + float im10 = (-m10 * l + m12 * i - m13 * h) * det; + float im11 = ( m00 * l - m02 * i + m03 * h) * det; + float im12 = (-m30 * f + m32 * c - m33 * b) * det; + float im13 = ( m20 * f - m22 * c + m23 * b) * det; + float im20 = ( m10 * k - m11 * i + m13 * g) * det; + float im21 = (-m00 * k + m01 * i - m03 * g) * det; + float im22 = ( m30 * e - m31 * c + m33 * a) * det; + float im23 = (-m20 * e + m21 * c - m23 * a) * det; + float im30 = (-m10 * j + m11 * h - m12 * g) * det; + float im31 = ( m00 * j - m01 * h + m02 * g) * det; + float im32 = (-m30 * d + m31 * b - m32 * a) * det; + float im33 = ( m20 * d - m21 * b + m22 * a) * det; + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + float ndcZ = winZ+winZ-1.0f; + float invW = 1.0f / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33); + return dest.set((im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW, + (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW, + (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW, + 1.0f); + } + + public Vector3f unproject(float winX, float winY, float winZ, int[] viewport, Vector3f dest) { + float a = m00 * m11 - m01 * m10; + float b = m00 * m12 - m02 * m10; + float c = m00 * m13 - m03 * m10; + float d = m01 * m12 - m02 * m11; + float e = m01 * m13 - m03 * m11; + float f = m02 * m13 - m03 * m12; + float g = m20 * m31 - m21 * m30; + float h = m20 * m32 - m22 * m30; + float i = m20 * m33 - m23 * m30; + float j = m21 * m32 - m22 * m31; + float k = m21 * m33 - m23 * m31; + float l = m22 * m33 - m23 * m32; + float det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0f / det; + float im00 = ( m11 * l - m12 * k + m13 * j) * det; + float im01 = (-m01 * l + m02 * k - m03 * j) * det; + float im02 = ( m31 * f - m32 * e + m33 * d) * det; + float im03 = (-m21 * f + m22 * e - m23 * d) * det; + float im10 = (-m10 * l + m12 * i - m13 * h) * det; + float im11 = ( m00 * l - m02 * i + m03 * h) * det; + float im12 = (-m30 * f + m32 * c - m33 * b) * det; + float im13 = ( m20 * f - m22 * c + m23 * b) * det; + float im20 = ( m10 * k - m11 * i + m13 * g) * det; + float im21 = (-m00 * k + m01 * i - m03 * g) * det; + float im22 = ( m30 * e - m31 * c + m33 * a) * det; + float im23 = (-m20 * e + m21 * c - m23 * a) * det; + float im30 = (-m10 * j + m11 * h - m12 * g) * det; + float im31 = ( m00 * j - m01 * h + m02 * g) * det; + float im32 = (-m30 * d + m31 * b - m32 * a) * det; + float im33 = ( m20 * d - m21 * b + m22 * a) * det; + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + float ndcZ = winZ+winZ-1.0f; + float invW = 1.0f / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33); + return dest.set((im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW, + (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW, + (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW); + } + + public Vector4f unproject(Vector3fc winCoords, int[] viewport, Vector4f dest) { + return unproject(winCoords.x(), winCoords.y(), winCoords.z(), viewport, dest); + } + + public Vector3f unproject(Vector3fc winCoords, int[] viewport, Vector3f dest) { + return unproject(winCoords.x(), winCoords.y(), winCoords.z(), viewport, dest); + } + + public Matrix4f unprojectRay(float winX, float winY, int[] viewport, Vector3f originDest, Vector3f dirDest) { + float a = m00 * m11 - m01 * m10; + float b = m00 * m12 - m02 * m10; + float c = m00 * m13 - m03 * m10; + float d = m01 * m12 - m02 * m11; + float e = m01 * m13 - m03 * m11; + float f = m02 * m13 - m03 * m12; + float g = m20 * m31 - m21 * m30; + float h = m20 * m32 - m22 * m30; + float i = m20 * m33 - m23 * m30; + float j = m21 * m32 - m22 * m31; + float k = m21 * m33 - m23 * m31; + float l = m22 * m33 - m23 * m32; + float det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0f / det; + float im00 = ( m11 * l - m12 * k + m13 * j) * det; + float im01 = (-m01 * l + m02 * k - m03 * j) * det; + float im02 = ( m31 * f - m32 * e + m33 * d) * det; + float im03 = (-m21 * f + m22 * e - m23 * d) * det; + float im10 = (-m10 * l + m12 * i - m13 * h) * det; + float im11 = ( m00 * l - m02 * i + m03 * h) * det; + float im12 = (-m30 * f + m32 * c - m33 * b) * det; + float im13 = ( m20 * f - m22 * c + m23 * b) * det; + float im20 = ( m10 * k - m11 * i + m13 * g) * det; + float im21 = (-m00 * k + m01 * i - m03 * g) * det; + float im22 = ( m30 * e - m31 * c + m33 * a) * det; + float im23 = (-m20 * e + m21 * c - m23 * a) * det; + float im30 = (-m10 * j + m11 * h - m12 * g) * det; + float im31 = ( m00 * j - m01 * h + m02 * g) * det; + float im32 = (-m30 * d + m31 * b - m32 * a) * det; + float im33 = ( m20 * d - m21 * b + m22 * a) * det; + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + float px = im00 * ndcX + im10 * ndcY + im30; + float py = im01 * ndcX + im11 * ndcY + im31; + float pz = im02 * ndcX + im12 * ndcY + im32; + float invNearW = 1.0f / (im03 * ndcX + im13 * ndcY - im23 + im33); + float nearX = (px - im20) * invNearW; + float nearY = (py - im21) * invNearW; + float nearZ = (pz - im22) * invNearW; + float invW0 = 1.0f / (im03 * ndcX + im13 * ndcY + im33); + float x0 = px * invW0; + float y0 = py * invW0; + float z0 = pz * invW0; + originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ; + dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ; + return this; + } + + public Matrix4f unprojectRay(Vector2fc winCoords, int[] viewport, Vector3f originDest, Vector3f dirDest) { + return unprojectRay(winCoords.x(), winCoords.y(), viewport, originDest, dirDest); + } + + public Vector4f unprojectInv(Vector3fc winCoords, int[] viewport, Vector4f dest) { + return unprojectInv(winCoords.x(), winCoords.y(), winCoords.z(), viewport, dest); + } + + public Vector4f unprojectInv(float winX, float winY, float winZ, int[] viewport, Vector4f dest) { + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + float ndcZ = winZ+winZ-1.0f; + float invW = 1.0f / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33); + return dest.set((m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW, + (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW, + (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW, + 1.0f); + } + + public Matrix4f unprojectInvRay(Vector2fc winCoords, int[] viewport, Vector3f originDest, Vector3f dirDest) { + return unprojectInvRay(winCoords.x(), winCoords.y(), viewport, originDest, dirDest); + } + + public Matrix4f unprojectInvRay(float winX, float winY, int[] viewport, Vector3f originDest, Vector3f dirDest) { + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + float px = m00 * ndcX + m10 * ndcY + m30; + float py = m01 * ndcX + m11 * ndcY + m31; + float pz = m02 * ndcX + m12 * ndcY + m32; + float invNearW = 1.0f / (m03 * ndcX + m13 * ndcY - m23 + m33); + float nearX = (px - m20) * invNearW; + float nearY = (py - m21) * invNearW; + float nearZ = (pz - m22) * invNearW; + float invW0 = 1.0f / (m03 * ndcX + m13 * ndcY + m33); + float x0 = px * invW0; + float y0 = py * invW0; + float z0 = pz * invW0; + originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ; + dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ; + return this; + } + + public Vector3f unprojectInv(Vector3fc winCoords, int[] viewport, Vector3f dest) { + return unprojectInv(winCoords.x(), winCoords.y(), winCoords.z(), viewport, dest); + } + + public Vector3f unprojectInv(float winX, float winY, float winZ, int[] viewport, Vector3f dest) { + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + float ndcZ = winZ+winZ-1.0f; + float invW = 1.0f / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33); + return dest.set((m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW, + (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW, + (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW); + } + + public Vector4f project(float x, float y, float z, int[] viewport, Vector4f winCoordsDest) { + float invW = 1.0f / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))); + float nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW; + float ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW; + float nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW; + return winCoordsDest.set(Math.fma(Math.fma(nx, 0.5f, 0.5f), viewport[2], viewport[0]), + Math.fma(Math.fma(ny, 0.5f, 0.5f), viewport[3], viewport[1]), + Math.fma(0.5f, nz, 0.5f), + 1.0f); + } + + public Vector3f project(float x, float y, float z, int[] viewport, Vector3f winCoordsDest) { + float invW = 1.0f / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))); + float nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW; + float ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW; + float nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW; + winCoordsDest.x = Math.fma(Math.fma(nx, 0.5f, 0.5f), viewport[2], viewport[0]); + winCoordsDest.y = Math.fma(Math.fma(ny, 0.5f, 0.5f), viewport[3], viewport[1]); + winCoordsDest.z = Math.fma(0.5f, nz, 0.5f); + return winCoordsDest; + } + + public Vector4f project(Vector3fc position, int[] viewport, Vector4f winCoordsDest) { + return project(position.x(), position.y(), position.z(), viewport, winCoordsDest); + } + + public Vector3f project(Vector3fc position, int[] viewport, Vector3f winCoordsDest) { + return project(position.x(), position.y(), position.z(), viewport, winCoordsDest); + } + + public Matrix4f reflect(float a, float b, float c, float d, Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.reflection(a, b, c, d); + else if ((properties & PROPERTY_AFFINE) != 0) + return reflectAffine(a, b, c, d, dest); + return reflectGeneric(a, b, c, d, dest); + } + private Matrix4f reflectAffine(float a, float b, float c, float d, Matrix4f dest) { + float da = a + a, db = b + b, dc = c + c, dd = d + d; + float rm00 = 1.0f - da * a; + float rm01 = -da * b; + float rm02 = -da * c; + float rm10 = -db * a; + float rm11 = 1.0f - db * b; + float rm12 = -db * c; + float rm20 = -dc * a; + float rm21 = -dc * b; + float rm22 = 1.0f - dc * c; + float rm30 = -dd * a; + float rm31 = -dd * b; + float rm32 = -dd * c; + // matrix multiplication + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m33); + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(0.0f) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0f) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0f) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + private Matrix4f reflectGeneric(float a, float b, float c, float d, Matrix4f dest) { + float da = a + a, db = b + b, dc = c + c, dd = d + d; + float rm00 = 1.0f - da * a; + float rm01 = -da * b; + float rm02 = -da * c; + float rm10 = -db * a; + float rm11 = 1.0f - db * b; + float rm12 = -db * c; + float rm20 = -dc * a; + float rm21 = -dc * b; + float rm22 = 1.0f - dc * c; + float rm30 = -dd * a; + float rm31 = -dd * b; + float rm32 = -dd * c; + // matrix multiplication + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33); + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + float nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4f reflect(float a, float b, float c, float d) { + return reflect(a, b, c, d, this); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @return this + */ + public Matrix4f reflect(float nx, float ny, float nz, float px, float py, float pz) { + return reflect(nx, ny, nz, px, py, pz, this); + } + + public Matrix4f reflect(float nx, float ny, float nz, float px, float py, float pz, Matrix4f dest) { + float invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz); + float nnx = nx * invLength; + float nny = ny * invLength; + float nnz = nz * invLength; + /* See: http://mathworld.wolfram.com/Plane.html */ + return reflect(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz, dest); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @return this + */ + public Matrix4f reflect(Vector3fc normal, Vector3fc point) { + return reflect(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z()); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation and a point on the plane. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation + * @param point + * a point on the plane + * @return this + */ + public Matrix4f reflect(Quaternionfc orientation, Vector3fc point) { + return reflect(orientation, point, this); + } + + public Matrix4f reflect(Quaternionfc orientation, Vector3fc point, Matrix4f dest) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + float normalX = (float) (orientation.x() * num3 + orientation.w() * num2); + float normalY = (float) (orientation.y() * num3 - orientation.w() * num1); + float normalZ = (float) (1.0 - (orientation.x() * num1 + orientation.y() * num2)); + return reflect(normalX, normalY, normalZ, point.x(), point.y(), point.z(), dest); + } + + public Matrix4f reflect(Vector3fc normal, Vector3fc point, Matrix4f dest) { + return reflect(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z(), dest); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4f reflection(float a, float b, float c, float d) { + float da = a + a, db = b + b, dc = c + c, dd = d + d; + this._m00(1.0f - da * a) + ._m01(-da * b) + ._m02(-da * c) + ._m03(0.0f) + ._m10(-db * a) + ._m11(1.0f - db * b) + ._m12(-db * c) + ._m13(0.0f) + ._m20(-dc * a) + ._m21(-dc * b) + ._m22(1.0f - dc * c) + ._m23(0.0f) + ._m30(-dd * a) + ._m31(-dd * b) + ._m32(-dd * c) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the plane normal and a point on the plane. + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @return this + */ + public Matrix4f reflection(float nx, float ny, float nz, float px, float py, float pz) { + float invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz); + float nnx = nx * invLength; + float nny = ny * invLength; + float nnz = nz * invLength; + /* See: http://mathworld.wolfram.com/Plane.html */ + return reflection(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the plane normal and a point on the plane. + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @return this + */ + public Matrix4f reflection(Vector3fc normal, Vector3fc point) { + return reflection(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z()); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about a plane + * specified via the plane orientation and a point on the plane. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + * + * @param orientation + * the plane orientation + * @param point + * a point on the plane + * @return this + */ + public Matrix4f reflection(Quaternionfc orientation, Vector3fc point) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + float normalX = (float) (orientation.x() * num3 + orientation.w() * num2); + float normalY = (float) (orientation.y() * num3 - orientation.w() * num1); + float normalZ = (float) (1.0 - (orientation.x() * num1 + orientation.y() * num2)); + return reflection(normalX, normalY, normalZ, point.x(), point.y(), point.z()); + } + + public Vector4f getRow(int row, Vector4f dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + return dest.set(m00, m10, m20, m30); + case 1: + return dest.set(m01, m11, m21, m31); + case 2: + return dest.set(m02, m12, m22, m32); + case 3: + return dest.set(m03, m13, m23, m33); + default: + throw new IndexOutOfBoundsException(); + } + } + + public Vector3f getRow(int row, Vector3f dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + return dest.set(m00, m10, m20); + case 1: + return dest.set(m01, m11, m21); + case 2: + return dest.set(m02, m12, m22); + case 3: + return dest.set(m03, m13, m23); + default: + throw new IndexOutOfBoundsException(); + } + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..3] + * @param src + * the row components to set + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..3] + */ + public Matrix4f setRow(int row, Vector4fc src) throws IndexOutOfBoundsException { + switch (row) { + case 0: + return _m00(src.x())._m10(src.y())._m20(src.z())._m30(src.w())._properties(0); + case 1: + return _m01(src.x())._m11(src.y())._m21(src.z())._m31(src.w())._properties(0); + case 2: + return _m02(src.x())._m12(src.y())._m22(src.z())._m32(src.w())._properties(0); + case 3: + return _m03(src.x())._m13(src.y())._m23(src.z())._m33(src.w())._properties(0); + default: + throw new IndexOutOfBoundsException(); + } + } + + public Vector4f getColumn(int column, Vector4f dest) throws IndexOutOfBoundsException { + return MemUtil.INSTANCE.getColumn(this, column, dest); + } + + public Vector3f getColumn(int column, Vector3f dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + return dest.set(m00, m01, m02); + case 1: + return dest.set(m10, m11, m12); + case 2: + return dest.set(m20, m21, m22); + case 3: + return dest.set(m30, m31, m32); + default: + throw new IndexOutOfBoundsException(); + } + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param src + * the column components to set + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + public Matrix4f setColumn(int column, Vector4fc src) throws IndexOutOfBoundsException { + if (src instanceof Vector4f) + return MemUtil.INSTANCE.setColumn((Vector4f) src, column, this)._properties(0); + return MemUtil.INSTANCE.setColumn(src, column, this)._properties(0); + } + + public float get(int column, int row) { + return MemUtil.INSTANCE.get(this, column, row); + } + + /** + * Set the matrix element at the given column and row to the specified value. + * + * @param column + * the colum index in [0..3] + * @param row + * the row index in [0..3] + * @param value + * the value + * @return this + */ + public Matrix4f set(int column, int row, float value) { + return MemUtil.INSTANCE.set(this, column, row, value); + } + + public float getRowColumn(int row, int column) { + return MemUtil.INSTANCE.get(this, column, row); + } + + /** + * Set the matrix element at the given row and column to the specified value. + * + * @param row + * the row index in [0..3] + * @param column + * the colum index in [0..3] + * @param value + * the value + * @return this + */ + public Matrix4f setRowColumn(int row, int column, float value) { + return MemUtil.INSTANCE.set(this, column, row, value); + } + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into the upper left 3x3 submatrix of this. + * All other values of this will be set to {@link #identity() identity}. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link #set3x3(Matrix4f)} to set a given Matrix4f to only the upper left 3x3 submatrix + * of this matrix. + * + * @see #set3x3(Matrix4f) + * + * @return this + */ + public Matrix4f normal() { + return normal(this); + } + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into the upper left 3x3 submatrix of dest. + * All other values of dest will be set to {@link #identity() identity}. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link #set3x3(Matrix4f)} to set a given Matrix4f to only the upper left 3x3 submatrix + * of this matrix. + * + * @see #set3x3(Matrix4f) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f normal(Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalOrthonormal(dest); + return normalGeneric(dest); + } + private Matrix4f normalOrthonormal(Matrix4f dest) { + if (dest != this) + dest.set(this); + return dest._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + } + private Matrix4f normalGeneric(Matrix4f dest) { + float m00m11 = m00 * m11; + float m01m10 = m01 * m10; + float m02m10 = m02 * m10; + float m00m12 = m00 * m12; + float m01m12 = m01 * m12; + float m02m11 = m02 * m11; + float det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + float s = 1.0f / det; + /* Invert and transpose in one go */ + float nm00 = (m11 * m22 - m21 * m12) * s; + float nm01 = (m20 * m12 - m10 * m22) * s; + float nm02 = (m10 * m21 - m20 * m11) * s; + float nm10 = (m21 * m02 - m01 * m22) * s; + float nm11 = (m00 * m22 - m20 * m02) * s; + float nm12 = (m20 * m01 - m00 * m21) * s; + float nm20 = (m01m12 - m02m11) * s; + float nm21 = (m02m10 - m00m12) * s; + float nm22 = (m00m11 - m01m10) * s; + return dest + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(0.0f) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0f) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f) + ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE)); + } + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link Matrix3f#set(Matrix4fc)} to set a given Matrix3f to only the upper left 3x3 submatrix + * of this matrix. + * + * @see Matrix3f#set(Matrix4fc) + * @see #get3x3(Matrix3f) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f normal(Matrix3f dest) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalOrthonormal(dest); + return normalGeneric(dest); + } + private Matrix3f normalOrthonormal(Matrix3f dest) { + dest.set(this); + return dest; + } + private Matrix3f normalGeneric(Matrix3f dest) { + float det = (m00 * m11 - (m01 * m10)) * m22 + + (m02 * m10 - (m00 * m12)) * m21 + + (m01 * m12 - (m02 * m11)) * m20; + float s = 1.0f / det; + /* Invert and transpose in one go */ + return dest._m00((m11 * m22 - m21 * m12) * s) + ._m01((m20 * m12 - m10 * m22) * s) + ._m02((m10 * m21 - m20 * m11) * s) + ._m10((m21 * m02 - m01 * m22) * s) + ._m11((m00 * m22 - m20 * m02) * s) + ._m12((m20 * m01 - m00 * m21) * s) + ._m20((m01 * m12 - m02 * m11) * s) + ._m21((m02 * m10 - m00 * m12) * s) + ._m22((m00 * m11 - m01 * m10) * s); + } + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this. + *

+ * The cofactor matrix can be used instead of {@link #normal()} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @return this + */ + public Matrix4f cofactor3x3() { + return cofactor3x3(this); + } + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this + * and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f cofactor3x3(Matrix3f dest) { + return dest._m00(m11 * m22 - m21 * m12) + ._m01(m20 * m12 - m10 * m22) + ._m02(m10 * m21 - m20 * m11) + ._m10(m21 * m02 - m01 * m22) + ._m11(m00 * m22 - m20 * m02) + ._m12(m20 * m01 - m00 * m21) + ._m20(m01 * m12 - m02 * m11) + ._m21(m02 * m10 - m00 * m12) + ._m22(m00 * m11 - m01 * m10); + } + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this + * and store it into dest. + * All other values of dest will be set to {@link #identity() identity}. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix4f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f cofactor3x3(Matrix4f dest) { + float nm10 = m21 * m02 - m01 * m22; + float nm11 = m00 * m22 - m20 * m02; + float nm12 = m20 * m01 - m00 * m21; + float nm20 = m01 * m12 - m11 * m02; + float nm21 = m02 * m10 - m12 * m00; + float nm22 = m00 * m11 - m10 * m01; + return dest + ._m00(m11 * m22 - m21 * m12) + ._m01(m20 * m12 - m10 * m22) + ._m02(m10 * m21 - m20 * m11) + ._m03(0.0f) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(0.0f) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f) + ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE)); + } + + /** + * Normalize the upper left 3x3 submatrix of this matrix. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @return this + */ + public Matrix4f normalize3x3() { + return normalize3x3(this); + } + + public Matrix4f normalize3x3(Matrix4f dest) { + float invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + float invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + float invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + return dest + ._m00(m00 * invXlen)._m01(m01 * invXlen)._m02(m02 * invXlen) + ._m10(m10 * invYlen)._m11(m11 * invYlen)._m12(m12 * invYlen) + ._m20(m20 * invZlen)._m21(m21 * invZlen)._m22(m22 * invZlen) + ._m30(m30)._m31(m31)._m32(m32)._m33(m33) + ._properties(properties); + } + + public Matrix3f normalize3x3(Matrix3f dest) { + float invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + float invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + float invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + return dest + ._m00(m00 * invXlen)._m01(m01 * invXlen)._m02(m02 * invXlen) + ._m10(m10 * invYlen)._m11(m11 * invYlen)._m12(m12 * invYlen) + ._m20(m20 * invZlen)._m21(m21 * invZlen)._m22(m22 * invZlen); + } + + public Vector4f frustumPlane(int plane, Vector4f dest) { + switch (plane) { + case PLANE_NX: + dest.set(m03 + m00, m13 + m10, m23 + m20, m33 + m30).normalize3(); + break; + case PLANE_PX: + dest.set(m03 - m00, m13 - m10, m23 - m20, m33 - m30).normalize3(); + break; + case PLANE_NY: + dest.set(m03 + m01, m13 + m11, m23 + m21, m33 + m31).normalize3(); + break; + case PLANE_PY: + dest.set(m03 - m01, m13 - m11, m23 - m21, m33 - m31).normalize3(); + break; + case PLANE_NZ: + dest.set(m03 + m02, m13 + m12, m23 + m22, m33 + m32).normalize3(); + break; + case PLANE_PZ: + dest.set(m03 - m02, m13 - m12, m23 - m22, m33 - m32).normalize3(); + break; + default: + throw new IllegalArgumentException("dest"); //$NON-NLS-1$ + } + return dest; + } + + public Vector3f frustumCorner(int corner, Vector3f point) { + float d1, d2, d3; + float n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z; + switch (corner) { + case CORNER_NXNYNZ: // left, bottom, near + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom + n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near + break; + case CORNER_PXNYNZ: // right, bottom, near + n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right + n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom + n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near + break; + case CORNER_PXPYNZ: // right, top, near + n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right + n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top + n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near + break; + case CORNER_NXPYNZ: // left, top, near + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top + n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near + break; + case CORNER_PXNYPZ: // right, bottom, far + n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right + n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom + n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far + break; + case CORNER_NXNYPZ: // left, bottom, far + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom + n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far + break; + case CORNER_NXPYPZ: // left, top, far + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top + n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far + break; + case CORNER_PXPYPZ: // right, top, far + n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right + n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top + n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far + break; + default: + throw new IllegalArgumentException("corner"); //$NON-NLS-1$ + } + float c23x, c23y, c23z; + c23x = n2y * n3z - n2z * n3y; + c23y = n2z * n3x - n2x * n3z; + c23z = n2x * n3y - n2y * n3x; + float c31x, c31y, c31z; + c31x = n3y * n1z - n3z * n1y; + c31y = n3z * n1x - n3x * n1z; + c31z = n3x * n1y - n3y * n1x; + float c12x, c12y, c12z; + c12x = n1y * n2z - n1z * n2y; + c12y = n1z * n2x - n1x * n2z; + c12z = n1x * n2y - n1y * n2x; + float invDot = 1.0f / (n1x * c23x + n1y * c23y + n1z * c23z); + point.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot; + point.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot; + point.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot; + return point; + } + + /** + * Compute the eye/origin of the perspective frustum transformation defined by this matrix, + * which can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given origin. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(float, float, float, float) perspective()} + * or {@link #frustum(float, float, float, float, float, float) frustum()}. + *

+ * Generally, this method computes the origin in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * This method is equivalent to calling: invert(new Matrix4f()).transformProject(0, 0, -1, 0, origin) + * and in the case of an already available inverse of this matrix, the method {@link #perspectiveInvOrigin(Vector3f)} + * on the inverse of the matrix should be used instead. + *

+ * Reference: http://geomalgorithms.com + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @see #perspectiveInvOrigin(Vector3f) + * + * @param origin + * will hold the origin of the coordinate system before applying this + * perspective projection transformation + * @return origin + */ + public Vector3f perspectiveOrigin(Vector3f origin) { + /* + * Simply compute the intersection point of the left, right and top frustum plane. + */ + float d1, d2, d3; + float n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z; + n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left + n2x = m03 - m00; n2y = m13 - m10; n2z = m23 - m20; d2 = m33 - m30; // right + n3x = m03 - m01; n3y = m13 - m11; n3z = m23 - m21; d3 = m33 - m31; // top + float c23x, c23y, c23z; + c23x = n2y * n3z - n2z * n3y; + c23y = n2z * n3x - n2x * n3z; + c23z = n2x * n3y - n2y * n3x; + float c31x, c31y, c31z; + c31x = n3y * n1z - n3z * n1y; + c31y = n3z * n1x - n3x * n1z; + c31z = n3x * n1y - n3y * n1x; + float c12x, c12y, c12z; + c12x = n1y * n2z - n1z * n2y; + c12y = n1z * n2x - n1x * n2z; + c12z = n1x * n2y - n1y * n2x; + float invDot = 1.0f / (n1x * c23x + n1y * c23y + n1z * c23z); + origin.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot; + origin.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot; + origin.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot; + return origin; + } + + /** + * Compute the eye/origin of the inverse of the perspective frustum transformation defined by this matrix, + * which can be the inverse of a projection matrix or the inverse of a combined modelview-projection matrix, and store the result + * in the given dest. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(float, float, float, float) perspective()} + * or {@link #frustum(float, float, float, float, float, float) frustum()}. + *

+ * If the inverse of the modelview-projection matrix is not available, then calling {@link #perspectiveOrigin(Vector3f)} + * on the original modelview-projection matrix is preferred. + * + * @see #perspectiveOrigin(Vector3f) + * + * @param dest + * will hold the result + * @return dest + */ + public Vector3f perspectiveInvOrigin(Vector3f dest) { + float invW = 1.0f / m23; + dest.x = m20 * invW; + dest.y = m21 * invW; + dest.z = m22 * invW; + return dest; + } + + /** + * Return the vertical field-of-view angle in radians of this perspective transformation matrix. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(float, float, float, float) perspective()} + * or {@link #frustum(float, float, float, float, float, float) frustum()}. + *

+ * For orthogonal transformations this method will return 0.0. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @return the vertical field-of-view angle in radians + */ + public float perspectiveFov() { + /* + * Compute the angle between the bottom and top frustum plane normals. + */ + float n1x, n1y, n1z, n2x, n2y, n2z; + n1x = m03 + m01; n1y = m13 + m11; n1z = m23 + m21; // bottom + n2x = m01 - m03; n2y = m11 - m13; n2z = m21 - m23; // top + float n1len = Math.sqrt(n1x * n1x + n1y * n1y + n1z * n1z); + float n2len = Math.sqrt(n2x * n2x + n2y * n2y + n2z * n2z); + return Math.acos((n1x * n2x + n1y * n2y + n1z * n2z) / (n1len * n2len)); + } + + /** + * Extract the near clip plane distance from this perspective projection matrix. + *

+ * This method only works if this is a perspective projection matrix, for example obtained via {@link #perspective(float, float, float, float)}. + * + * @return the near clip plane distance + */ + public float perspectiveNear() { + return m32 / (m23 + m22); + } + + /** + * Extract the far clip plane distance from this perspective projection matrix. + *

+ * This method only works if this is a perspective projection matrix, for example obtained via {@link #perspective(float, float, float, float)}. + * + * @return the far clip plane distance + */ + public float perspectiveFar() { + return m32 / (m22 - m23); + } + + public Vector3f frustumRayDir(float x, float y, Vector3f dir) { + /* + * This method works by first obtaining the frustum plane normals, + * then building the cross product to obtain the corner rays, + * and finally bilinearly interpolating to obtain the desired direction. + * The code below uses a condense form of doing all this making use + * of some mathematical identities to simplify the overall expression. + */ + float a = m10 * m23, b = m13 * m21, c = m10 * m21, d = m11 * m23, e = m13 * m20, f = m11 * m20; + float g = m03 * m20, h = m01 * m23, i = m01 * m20, j = m03 * m21, k = m00 * m23, l = m00 * m21; + float m = m00 * m13, n = m03 * m11, o = m00 * m11, p = m01 * m13, q = m03 * m10, r = m01 * m10; + float m1x, m1y, m1z; + m1x = (d + e + f - a - b - c) * (1.0f - y) + (a - b - c + d - e + f) * y; + m1y = (j + k + l - g - h - i) * (1.0f - y) + (g - h - i + j - k + l) * y; + m1z = (p + q + r - m - n - o) * (1.0f - y) + (m - n - o + p - q + r) * y; + float m2x, m2y, m2z; + m2x = (b - c - d + e + f - a) * (1.0f - y) + (a + b - c - d - e + f) * y; + m2y = (h - i - j + k + l - g) * (1.0f - y) + (g + h - i - j - k + l) * y; + m2z = (n - o - p + q + r - m) * (1.0f - y) + (m + n - o - p - q + r) * y; + dir.x = m1x + (m2x - m1x) * x; + dir.y = m1y + (m2y - m1y) * x; + dir.z = m1z + (m2z - m1z) * x; + return dir.normalize(dir); + } + + public Vector3f positiveZ(Vector3f dir) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalizedPositiveZ(dir); + return positiveZGeneric(dir); + } + private Vector3f positiveZGeneric(Vector3f dir) { + return dir.set(m10 * m21 - m11 * m20, m20 * m01 - m21 * m00, m00 * m11 - m01 * m10).normalize(); + } + + public Vector3f normalizedPositiveZ(Vector3f dir) { + return dir.set(m02, m12, m22); + } + + public Vector3f positiveX(Vector3f dir) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalizedPositiveX(dir); + return positiveXGeneric(dir); + } + private Vector3f positiveXGeneric(Vector3f dir) { + return dir.set(m11 * m22 - m12 * m21, m02 * m21 - m01 * m22, m01 * m12 - m02 * m11).normalize(); + } + + public Vector3f normalizedPositiveX(Vector3f dir) { + return dir.set(m00, m10, m20); + } + + public Vector3f positiveY(Vector3f dir) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalizedPositiveY(dir); + return positiveYGeneric(dir); + } + private Vector3f positiveYGeneric(Vector3f dir) { + return dir.set(m12 * m20 - m10 * m22, m00 * m22 - m02 * m20, m02 * m10 - m00 * m12).normalize(); + } + + public Vector3f normalizedPositiveY(Vector3f dir) { + return dir.set(m01, m11, m21); + } + + public Vector3f originAffine(Vector3f origin) { + float a = m00 * m11 - m01 * m10; + float b = m00 * m12 - m02 * m10; + float d = m01 * m12 - m02 * m11; + float g = m20 * m31 - m21 * m30; + float h = m20 * m32 - m22 * m30; + float j = m21 * m32 - m22 * m31; + return origin.set(-m10 * j + m11 * h - m12 * g, m00 * j - m01 * h + m02 * g, -m30 * d + m31 * b - m32 * a); + } + + public Vector3f origin(Vector3f dest) { + if ((properties & PROPERTY_AFFINE) != 0) + return originAffine(dest); + return originGeneric(dest); + } + private Vector3f originGeneric(Vector3f dest) { + float a = m00 * m11 - m01 * m10; + float b = m00 * m12 - m02 * m10; + float c = m00 * m13 - m03 * m10; + float d = m01 * m12 - m02 * m11; + float e = m01 * m13 - m03 * m11; + float f = m02 * m13 - m03 * m12; + float g = m20 * m31 - m21 * m30; + float h = m20 * m32 - m22 * m30; + float i = m20 * m33 - m23 * m30; + float j = m21 * m32 - m22 * m31; + float k = m21 * m33 - m23 * m31; + float l = m22 * m33 - m23 * m32; + float det = a * l - b * k + c * j + d * i - e * h + f * g; + float invDet = 1.0f / det; + float nm30 = (-m10 * j + m11 * h - m12 * g) * invDet; + float nm31 = ( m00 * j - m01 * h + m02 * g) * invDet; + float nm32 = (-m30 * d + m31 * b - m32 * a) * invDet; + float nm33 = det / ( m20 * d - m21 * b + m22 * a); + return dest.set(nm30 * nm33, nm31 * nm33, nm32 * nm33); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction light. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param light + * the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4f shadow(Vector4f light, float a, float b, float c, float d) { + return shadow(light.x, light.y, light.z, light.w, a, b, c, d, this); + } + + public Matrix4f shadow(Vector4f light, float a, float b, float c, float d, Matrix4f dest) { + return shadow(light.x, light.y, light.z, light.w, a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW). + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param lightX + * the x-component of the light's vector + * @param lightY + * the y-component of the light's vector + * @param lightZ + * the z-component of the light's vector + * @param lightW + * the w-component of the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4f shadow(float lightX, float lightY, float lightZ, float lightW, float a, float b, float c, float d) { + return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, this); + } + + public Matrix4f shadow(float lightX, float lightY, float lightZ, float lightW, float a, float b, float c, float d, Matrix4f dest) { + // normalize plane + float invPlaneLen = Math.invsqrt(a*a + b*b + c*c); + float an = a * invPlaneLen; + float bn = b * invPlaneLen; + float cn = c * invPlaneLen; + float dn = d * invPlaneLen; + + float dot = an * lightX + bn * lightY + cn * lightZ + dn * lightW; + + // compute right matrix elements + float rm00 = dot - an * lightX; + float rm01 = -an * lightY; + float rm02 = -an * lightZ; + float rm03 = -an * lightW; + float rm10 = -bn * lightX; + float rm11 = dot - bn * lightY; + float rm12 = -bn * lightZ; + float rm13 = -bn * lightW; + float rm20 = -cn * lightX; + float rm21 = -cn * lightY; + float rm22 = dot - cn * lightZ; + float rm23 = -cn * lightW; + float rm30 = -dn * lightX; + float rm31 = -dn * lightY; + float rm32 = -dn * lightZ; + float rm33 = dot - dn * lightW; + + // matrix multiplication + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02 + m30 * rm03; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02 + m31 * rm03; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02 + m32 * rm03; + float nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02 + m33 * rm03; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12 + m30 * rm13; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12 + m31 * rm13; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12 + m32 * rm13; + float nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12 + m33 * rm13; + float nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30 * rm23; + float nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31 * rm23; + float nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32 * rm23; + float nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33 * rm23; + dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30 * rm33) + ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31 * rm33) + ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32 * rm33) + ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33 * rm33) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + public Matrix4f shadow(Vector4f light, Matrix4fc planeTransform, Matrix4f dest) { + // compute plane equation by transforming (y = 0) + float a = planeTransform.m10(); + float b = planeTransform.m11(); + float c = planeTransform.m12(); + float d = -a * planeTransform.m30() - b * planeTransform.m31() - c * planeTransform.m32(); + return shadow(light.x, light.y, light.z, light.w, a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction light. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param light + * the light's vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @return this + */ + public Matrix4f shadow(Vector4f light, Matrix4f planeTransform) { + return shadow(light, planeTransform, this); + } + + public Matrix4f shadow(float lightX, float lightY, float lightZ, float lightW, Matrix4fc planeTransform, Matrix4f dest) { + // compute plane equation by transforming (y = 0) + float a = planeTransform.m10(); + float b = planeTransform.m11(); + float c = planeTransform.m12(); + float d = -a * planeTransform.m30() - b * planeTransform.m31() - c * planeTransform.m32(); + return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW). + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param lightX + * the x-component of the light vector + * @param lightY + * the y-component of the light vector + * @param lightZ + * the z-component of the light vector + * @param lightW + * the w-component of the light vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @return this + */ + public Matrix4f shadow(float lightX, float lightY, float lightZ, float lightW, Matrix4f planeTransform) { + return shadow(lightX, lightY, lightZ, lightW, planeTransform, this); + } + + /** + * Set this matrix to a cylindrical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos while constraining a cylindrical rotation around the given up vector. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @param up + * the rotation axis (must be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix4f billboardCylindrical(Vector3fc objPos, Vector3fc targetPos, Vector3fc up) { + float dirX = targetPos.x() - objPos.x(); + float dirY = targetPos.y() - objPos.y(); + float dirZ = targetPos.z() - objPos.z(); + // left = up x dir + float leftX = up.y() * dirZ - up.z() * dirY; + float leftY = up.z() * dirX - up.x() * dirZ; + float leftZ = up.x() * dirY - up.y() * dirX; + // normalize left + float invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLen; + leftY *= invLeftLen; + leftZ *= invLeftLen; + // recompute dir by constraining rotation around 'up' + // dir = left x up + dirX = leftY * up.z() - leftZ * up.y(); + dirY = leftZ * up.x() - leftX * up.z(); + dirZ = leftX * up.y() - leftY * up.x(); + // normalize dir + float invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLen; + dirY *= invDirLen; + dirZ *= invDirLen; + // set matrix elements + this._m00(leftX) + ._m01(leftY) + ._m02(leftZ) + ._m03(0.0f) + ._m10(up.x()) + ._m11(up.y()) + ._m12(up.z()) + ._m13(0.0f) + ._m20(dirX) + ._m21(dirY) + ._m22(dirZ) + ._m23(0.0f) + ._m30(objPos.x()) + ._m31(objPos.y()) + ._m32(objPos.z()) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + *

+ * If preserving an up vector is not necessary when rotating the +Z axis, then a shortest arc rotation can be obtained + * using {@link #billboardSpherical(Vector3fc, Vector3fc)}. + * + * @see #billboardSpherical(Vector3fc, Vector3fc) + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @param up + * the up axis used to orient the object + * @return this + */ + public Matrix4f billboardSpherical(Vector3fc objPos, Vector3fc targetPos, Vector3fc up) { + float dirX = targetPos.x() - objPos.x(); + float dirY = targetPos.y() - objPos.y(); + float dirZ = targetPos.z() - objPos.z(); + // normalize dir + float invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLen; + dirY *= invDirLen; + dirZ *= invDirLen; + // left = up x dir + float leftX = up.y() * dirZ - up.z() * dirY; + float leftY = up.z() * dirX - up.x() * dirZ; + float leftZ = up.x() * dirY - up.y() * dirX; + // normalize left + float invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLen; + leftY *= invLeftLen; + leftZ *= invLeftLen; + // up = dir x left + float upX = dirY * leftZ - dirZ * leftY; + float upY = dirZ * leftX - dirX * leftZ; + float upZ = dirX * leftY - dirY * leftX; + // set matrix elements + this._m00(leftX) + ._m01(leftY) + ._m02(leftZ) + ._m03(0.0f) + ._m10(upX) + ._m11(upY) + ._m12(upZ) + ._m13(0.0f) + ._m20(dirX) + ._m21(dirY) + ._m22(dirZ) + ._m23(0.0f) + ._m30(objPos.x()) + ._m31(objPos.y()) + ._m32(objPos.z()) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos using a shortest arc rotation by not preserving any up vector of the object. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + *

+ * In order to specify an up vector which needs to be maintained when rotating the +Z axis of the object, + * use {@link #billboardSpherical(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #billboardSpherical(Vector3fc, Vector3fc, Vector3fc) + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @return this + */ + public Matrix4f billboardSpherical(Vector3fc objPos, Vector3fc targetPos) { + float toDirX = targetPos.x() - objPos.x(); + float toDirY = targetPos.y() - objPos.y(); + float toDirZ = targetPos.z() - objPos.z(); + float x = -toDirY; + float y = toDirX; + float w = Math.sqrt(toDirX * toDirX + toDirY * toDirY + toDirZ * toDirZ) + toDirZ; + float invNorm = Math.invsqrt(x * x + y * y + w * w); + x *= invNorm; + y *= invNorm; + w *= invNorm; + float q00 = (x + x) * x; + float q11 = (y + y) * y; + float q01 = (x + x) * y; + float q03 = (x + x) * w; + float q13 = (y + y) * w; + this._m00(1.0f - q11) + ._m01(q01) + ._m02(-q13) + ._m03(0.0f) + ._m10(q01) + ._m11(1.0f - q00) + ._m12(q03) + ._m13(0.0f) + ._m20(q13) + ._m21(-q03) + ._m22(1.0f - q11 - q00) + ._m23(0.0f) + ._m30(objPos.x()) + ._m31(objPos.y()) + ._m32(objPos.z()) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(m00); + result = prime * result + Float.floatToIntBits(m01); + result = prime * result + Float.floatToIntBits(m02); + result = prime * result + Float.floatToIntBits(m03); + result = prime * result + Float.floatToIntBits(m10); + result = prime * result + Float.floatToIntBits(m11); + result = prime * result + Float.floatToIntBits(m12); + result = prime * result + Float.floatToIntBits(m13); + result = prime * result + Float.floatToIntBits(m20); + result = prime * result + Float.floatToIntBits(m21); + result = prime * result + Float.floatToIntBits(m22); + result = prime * result + Float.floatToIntBits(m23); + result = prime * result + Float.floatToIntBits(m30); + result = prime * result + Float.floatToIntBits(m31); + result = prime * result + Float.floatToIntBits(m32); + result = prime * result + Float.floatToIntBits(m33); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof Matrix4f)) + return false; + Matrix4fc other = (Matrix4fc) obj; + if (Float.floatToIntBits(m00) != Float.floatToIntBits(other.m00())) + return false; + if (Float.floatToIntBits(m01) != Float.floatToIntBits(other.m01())) + return false; + if (Float.floatToIntBits(m02) != Float.floatToIntBits(other.m02())) + return false; + if (Float.floatToIntBits(m03) != Float.floatToIntBits(other.m03())) + return false; + if (Float.floatToIntBits(m10) != Float.floatToIntBits(other.m10())) + return false; + if (Float.floatToIntBits(m11) != Float.floatToIntBits(other.m11())) + return false; + if (Float.floatToIntBits(m12) != Float.floatToIntBits(other.m12())) + return false; + if (Float.floatToIntBits(m13) != Float.floatToIntBits(other.m13())) + return false; + if (Float.floatToIntBits(m20) != Float.floatToIntBits(other.m20())) + return false; + if (Float.floatToIntBits(m21) != Float.floatToIntBits(other.m21())) + return false; + if (Float.floatToIntBits(m22) != Float.floatToIntBits(other.m22())) + return false; + if (Float.floatToIntBits(m23) != Float.floatToIntBits(other.m23())) + return false; + if (Float.floatToIntBits(m30) != Float.floatToIntBits(other.m30())) + return false; + if (Float.floatToIntBits(m31) != Float.floatToIntBits(other.m31())) + return false; + if (Float.floatToIntBits(m32) != Float.floatToIntBits(other.m32())) + return false; + if (Float.floatToIntBits(m33) != Float.floatToIntBits(other.m33())) + return false; + return true; + } + + public boolean equals(Matrix4fc m, float delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix4f)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m02, m.m02(), delta)) + return false; + if (!Runtime.equals(m03, m.m03(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + if (!Runtime.equals(m12, m.m12(), delta)) + return false; + if (!Runtime.equals(m13, m.m13(), delta)) + return false; + if (!Runtime.equals(m20, m.m20(), delta)) + return false; + if (!Runtime.equals(m21, m.m21(), delta)) + return false; + if (!Runtime.equals(m22, m.m22(), delta)) + return false; + if (!Runtime.equals(m23, m.m23(), delta)) + return false; + if (!Runtime.equals(m30, m.m30(), delta)) + return false; + if (!Runtime.equals(m31, m.m31(), delta)) + return false; + if (!Runtime.equals(m32, m.m32(), delta)) + return false; + if (!Runtime.equals(m33, m.m33(), delta)) + return false; + return true; + } + + public Matrix4f pick(float x, float y, float width, float height, int[] viewport, Matrix4f dest) { + float sx = viewport[2] / width; + float sy = viewport[3] / height; + float tx = (viewport[2] + 2.0f * (viewport[0] - x)) / width; + float ty = (viewport[3] + 2.0f * (viewport[1] - y)) / height; + dest._m30(m00 * tx + m10 * ty + m30) + ._m31(m01 * tx + m11 * ty + m31) + ._m32(m02 * tx + m12 * ty + m32) + ._m33(m03 * tx + m13 * ty + m33) + ._m00(m00 * sx) + ._m01(m01 * sx) + ._m02(m02 * sx) + ._m03(m03 * sx) + ._m10(m10 * sy) + ._m11(m11 * sy) + ._m12(m12 * sy) + ._m13(m13 * sy) + ._properties(0); + return dest; + } + + /** + * Apply a picking transformation to this matrix using the given window coordinates (x, y) as the pick center + * and the given (width, height) as the size of the picking region in window coordinates. + * + * @param x + * the x coordinate of the picking region center in window coordinates + * @param y + * the y coordinate of the picking region center in window coordinates + * @param width + * the width of the picking region in window coordinates + * @param height + * the height of the picking region in window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @return this + */ + public Matrix4f pick(float x, float y, float width, float height, int[] viewport) { + return pick(x, y, width, height, viewport, this); + } + + public boolean isAffine() { + return m03 == 0.0f && m13 == 0.0f && m23 == 0.0f && m33 == 1.0f; + } + + /** + * Exchange the values of this matrix with the given other matrix. + * + * @param other + * the other matrix to exchange the values with + * @return this + */ + public Matrix4f swap(Matrix4f other) { + MemUtil.INSTANCE.swap(this, other); + int props = properties; + this.properties = other.properties(); + other.properties = props; + return this; + } + + public Matrix4f arcball(float radius, float centerX, float centerY, float centerZ, float angleX, float angleY, Matrix4f dest) { + float m30 = m20 * -radius + this.m30; + float m31 = m21 * -radius + this.m31; + float m32 = m22 * -radius + this.m32; + float m33 = m23 * -radius + this.m33; + float sin = Math.sin(angleX); + float cos = Math.cosFromSin(sin, angleX); + float nm10 = m10 * cos + m20 * sin; + float nm11 = m11 * cos + m21 * sin; + float nm12 = m12 * cos + m22 * sin; + float nm13 = m13 * cos + m23 * sin; + float m20 = this.m20 * cos - m10 * sin; + float m21 = this.m21 * cos - m11 * sin; + float m22 = this.m22 * cos - m12 * sin; + float m23 = this.m23 * cos - m13 * sin; + sin = Math.sin(angleY); + cos = Math.cosFromSin(sin, angleY); + float nm00 = m00 * cos - m20 * sin; + float nm01 = m01 * cos - m21 * sin; + float nm02 = m02 * cos - m22 * sin; + float nm03 = m03 * cos - m23 * sin; + float nm20 = m00 * sin + m20 * cos; + float nm21 = m01 * sin + m21 * cos; + float nm22 = m02 * sin + m22 * cos; + float nm23 = m03 * sin + m23 * cos; + dest._m30(-nm00 * centerX - nm10 * centerY - nm20 * centerZ + m30) + ._m31(-nm01 * centerX - nm11 * centerY - nm21 * centerZ + m31) + ._m32(-nm02 * centerX - nm12 * centerY - nm22 * centerZ + m32) + ._m33(-nm03 * centerX - nm13 * centerY - nm23 * centerZ + m33) + ._m20(nm20) + ._m21(nm21) + ._m22(nm22) + ._m23(nm23) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + public Matrix4f arcball(float radius, Vector3fc center, float angleX, float angleY, Matrix4f dest) { + return arcball(radius, center.x(), center.y(), center.z(), angleX, angleY, dest); + } + + /** + * Apply an arcball view transformation to this matrix with the given radius and center (centerX, centerY, centerZ) + * position of the arcball and the specified X and Y rotation angles. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ) + * + * @param radius + * the arcball radius + * @param centerX + * the x coordinate of the center position of the arcball + * @param centerY + * the y coordinate of the center position of the arcball + * @param centerZ + * the z coordinate of the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @return this + */ + public Matrix4f arcball(float radius, float centerX, float centerY, float centerZ, float angleX, float angleY) { + return arcball(radius, centerX, centerY, centerZ, angleX, angleY, this); + } + + /** + * Apply an arcball view transformation to this matrix with the given radius and center + * position of the arcball and the specified X and Y rotation angles. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z) + * + * @param radius + * the arcball radius + * @param center + * the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @return this + */ + public Matrix4f arcball(float radius, Vector3fc center, float angleX, float angleY) { + return arcball(radius, center.x(), center.y(), center.z(), angleX, angleY, this); + } + + /** + * Compute the axis-aligned bounding box of the frustum described by this matrix and store the minimum corner + * coordinates in the given min and the maximum corner coordinates in the given max vector. + *

+ * The matrix this is assumed to be the {@link #invert() inverse} of the origial view-projection matrix + * for which to compute the axis-aligned bounding box in world-space. + *

+ * The axis-aligned bounding box of the unit frustum is (-1, -1, -1), (1, 1, 1). + * + * @param min + * will hold the minimum corner coordinates of the axis-aligned bounding box + * @param max + * will hold the maximum corner coordinates of the axis-aligned bounding box + * @return this + */ + public Matrix4f frustumAabb(Vector3f min, Vector3f max) { + float minX = Float.POSITIVE_INFINITY; + float minY = Float.POSITIVE_INFINITY; + float minZ = Float.POSITIVE_INFINITY; + float maxX = Float.NEGATIVE_INFINITY; + float maxY = Float.NEGATIVE_INFINITY; + float maxZ = Float.NEGATIVE_INFINITY; + for (int t = 0; t < 8; t++) { + float x = ((t & 1) << 1) - 1.0f; + float y = (((t >>> 1) & 1) << 1) - 1.0f; + float z = (((t >>> 2) & 1) << 1) - 1.0f; + float invW = 1.0f / (m03 * x + m13 * y + m23 * z + m33); + float nx = (m00 * x + m10 * y + m20 * z + m30) * invW; + float ny = (m01 * x + m11 * y + m21 * z + m31) * invW; + float nz = (m02 * x + m12 * y + m22 * z + m32) * invW; + minX = minX < nx ? minX : nx; + minY = minY < ny ? minY : ny; + minZ = minZ < nz ? minZ : nz; + maxX = maxX > nx ? maxX : nx; + maxY = maxY > ny ? maxY : ny; + maxZ = maxZ > nz ? maxZ : nz; + } + min.x = minX; + min.y = minY; + min.z = minZ; + max.x = maxX; + max.y = maxY; + max.z = maxZ; + return this; + } + + public Matrix4f projectedGridRange(Matrix4fc projector, float sLower, float sUpper, Matrix4f dest) { + // Compute intersection with frustum edges and plane + float minX = Float.POSITIVE_INFINITY, minY = Float.POSITIVE_INFINITY; + float maxX = Float.NEGATIVE_INFINITY, maxY = Float.NEGATIVE_INFINITY; + boolean intersection = false; + for (int t = 0; t < 3 * 4; t++) { + float c0X, c0Y, c0Z; + float c1X, c1Y, c1Z; + if (t < 4) { + // all x edges + c0X = -1; c1X = +1; + c0Y = c1Y = ((t & 1) << 1) - 1.0f; + c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0f; + } else if (t < 8) { + // all y edges + c0Y = -1; c1Y = +1; + c0X = c1X = ((t & 1) << 1) - 1.0f; + c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0f; + } else { + // all z edges + c0Z = -1; c1Z = +1; + c0X = c1X = ((t & 1) << 1) - 1.0f; + c0Y = c1Y = (((t >>> 1) & 1) << 1) - 1.0f; + } + // unproject corners + float invW = 1.0f / (m03 * c0X + m13 * c0Y + m23 * c0Z + m33); + float p0x = (m00 * c0X + m10 * c0Y + m20 * c0Z + m30) * invW; + float p0y = (m01 * c0X + m11 * c0Y + m21 * c0Z + m31) * invW; + float p0z = (m02 * c0X + m12 * c0Y + m22 * c0Z + m32) * invW; + invW = 1.0f / (m03 * c1X + m13 * c1Y + m23 * c1Z + m33); + float p1x = (m00 * c1X + m10 * c1Y + m20 * c1Z + m30) * invW; + float p1y = (m01 * c1X + m11 * c1Y + m21 * c1Z + m31) * invW; + float p1z = (m02 * c1X + m12 * c1Y + m22 * c1Z + m32) * invW; + float dirX = p1x - p0x; + float dirY = p1y - p0y; + float dirZ = p1z - p0z; + float invDenom = 1.0f / dirY; + // test for intersection + for (int s = 0; s < 2; s++) { + float isectT = -(p0y + (s == 0 ? sLower : sUpper)) * invDenom; + if (isectT >= 0.0f && isectT <= 1.0f) { + intersection = true; + // project with projector matrix + float ix = p0x + isectT * dirX; + float iz = p0z + isectT * dirZ; + invW = 1.0f / (projector.m03() * ix + projector.m23() * iz + projector.m33()); + float px = (projector.m00() * ix + projector.m20() * iz + projector.m30()) * invW; + float py = (projector.m01() * ix + projector.m21() * iz + projector.m31()) * invW; + minX = minX < px ? minX : px; + minY = minY < py ? minY : py; + maxX = maxX > px ? maxX : px; + maxY = maxY > py ? maxY : py; + } + } + } + if (!intersection) + return null; // <- projected grid is not visible + dest.set(maxX - minX, 0, 0, 0, 0, maxY - minY, 0, 0, 0, 0, 1, 0, minX, minY, 0, 1); + dest._properties(PROPERTY_AFFINE); + return dest; + } + + /** + * Change the near and far clip plane distances of this perspective frustum transformation matrix + * and store the result in dest. + *

+ * This method only works if this is a perspective projection frustum transformation, for example obtained + * via {@link #perspective(float, float, float, float) perspective()} or {@link #frustum(float, float, float, float, float, float) frustum()}. + * + * @see #perspective(float, float, float, float) + * @see #frustum(float, float, float, float, float, float) + * + * @param near + * the new near clip plane distance + * @param far + * the new far clip plane distance + * @param dest + * will hold the resulting matrix + * @return dest + */ + public Matrix4f perspectiveFrustumSlice(float near, float far, Matrix4f dest) { + float invOldNear = (m23 + m22) / m32; + float invNearFar = 1.0f / (near - far); + dest._m00(m00 * invOldNear * near) + ._m01(m01) + ._m02(m02) + ._m03(m03) + ._m10(m10) + ._m11(m11 * invOldNear * near) + ._m12(m12) + ._m13(m13) + ._m20(m20) + ._m21(m21) + ._m22((far + near) * invNearFar) + ._m23(m23) + ._m30(m30) + ._m31(m31) + ._m32((far + far) * near * invNearFar) + ._m33(m33) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL)); + return dest; + } + + /** + * Build an ortographic projection transformation that fits the view-projection transformation represented by this + * into the given affine view transformation. + *

+ * The transformation represented by this must be given as the {@link #invert() inverse} of a typical combined camera view-projection + * transformation, whose projection can be either orthographic or perspective. + *

+ * The view must be an {@link #isAffine() affine} transformation which in the application of Cascaded Shadow Maps is usually the light view transformation. + * It be obtained via any affine transformation or for example via {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt()}. + *

+ * Reference: OpenGL SDK - Cascaded Shadow Maps + * + * @param view + * the view transformation to build a corresponding orthographic projection to fit the frustum of this + * @param dest + * will hold the crop projection transformation + * @return dest + */ + public Matrix4f orthoCrop(Matrix4fc view, Matrix4f dest) { + // determine min/max world z and min/max orthographically view-projected x/y + float minX = Float.POSITIVE_INFINITY, maxX = Float.NEGATIVE_INFINITY; + float minY = Float.POSITIVE_INFINITY, maxY = Float.NEGATIVE_INFINITY; + float minZ = Float.POSITIVE_INFINITY, maxZ = Float.NEGATIVE_INFINITY; + for (int t = 0; t < 8; t++) { + float x = ((t & 1) << 1) - 1.0f; + float y = (((t >>> 1) & 1) << 1) - 1.0f; + float z = (((t >>> 2) & 1) << 1) - 1.0f; + float invW = 1.0f / (m03 * x + m13 * y + m23 * z + m33); + float wx = (m00 * x + m10 * y + m20 * z + m30) * invW; + float wy = (m01 * x + m11 * y + m21 * z + m31) * invW; + float wz = (m02 * x + m12 * y + m22 * z + m32) * invW; + invW = 1.0f / (view.m03() * wx + view.m13() * wy + view.m23() * wz + view.m33()); + float vx = view.m00() * wx + view.m10() * wy + view.m20() * wz + view.m30(); + float vy = view.m01() * wx + view.m11() * wy + view.m21() * wz + view.m31(); + float vz = (view.m02() * wx + view.m12() * wy + view.m22() * wz + view.m32()) * invW; + minX = minX < vx ? minX : vx; + maxX = maxX > vx ? maxX : vx; + minY = minY < vy ? minY : vy; + maxY = maxY > vy ? maxY : vy; + minZ = minZ < vz ? minZ : vz; + maxZ = maxZ > vz ? maxZ : vz; + } + // build crop projection matrix to fit 'this' frustum into view + return dest.setOrtho(minX, maxX, minY, maxY, -maxZ, -minZ); + } + + /** + * Set this matrix to a perspective transformation that maps the trapezoid spanned by the four corner coordinates + * (p0x, p0y), (p1x, p1y), (p2x, p2y) and (p3x, p3y) to the unit square [(-1, -1)..(+1, +1)]. + *

+ * The corner coordinates are given in counter-clockwise order starting from the left corner on the smaller parallel side of the trapezoid + * seen when looking at the trapezoid oriented with its shorter parallel edge at the bottom and its longer parallel edge at the top. + *

+ * Reference: Trapezoidal Shadow Maps (TSM) - Recipe + * + * @param p0x + * the x coordinate of the left corner at the shorter edge of the trapezoid + * @param p0y + * the y coordinate of the left corner at the shorter edge of the trapezoid + * @param p1x + * the x coordinate of the right corner at the shorter edge of the trapezoid + * @param p1y + * the y coordinate of the right corner at the shorter edge of the trapezoid + * @param p2x + * the x coordinate of the right corner at the longer edge of the trapezoid + * @param p2y + * the y coordinate of the right corner at the longer edge of the trapezoid + * @param p3x + * the x coordinate of the left corner at the longer edge of the trapezoid + * @param p3y + * the y coordinate of the left corner at the longer edge of the trapezoid + * @return this + */ + public Matrix4f trapezoidCrop(float p0x, float p0y, float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + float aX = p1y - p0y, aY = p0x - p1x; + float nm00 = aY; + float nm10 = -aX; + float nm30 = aX * p0y - aY * p0x; + float nm01 = aX; + float nm11 = aY; + float nm31 = -(aX * p0x + aY * p0y); + float c3x = nm00 * p3x + nm10 * p3y + nm30; + float c3y = nm01 * p3x + nm11 * p3y + nm31; + float s = -c3x / c3y; + nm00 += s * nm01; + nm10 += s * nm11; + nm30 += s * nm31; + float d1x = nm00 * p1x + nm10 * p1y + nm30; + float d2x = nm00 * p2x + nm10 * p2y + nm30; + float d = d1x * c3y / (d2x - d1x); + nm31 += d; + float sx = 2.0f / d2x; + float sy = 1.0f / (c3y + d); + float u = (sy + sy) * d / (1.0f - sy * d); + float m03 = nm01 * sy; + float m13 = nm11 * sy; + float m33 = nm31 * sy; + nm01 = (u + 1.0f) * m03; + nm11 = (u + 1.0f) * m13; + nm31 = (u + 1.0f) * m33 - u; + nm00 = sx * nm00 - m03; + nm10 = sx * nm10 - m13; + nm30 = sx * nm30 - m33; + set(nm00, nm01, 0, m03, + nm10, nm11, 0, m13, + 0, 0, 1, 0, + nm30, nm31, 0, m33); + _properties(0); + return this; + } + + public Matrix4f transformAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, Vector3f outMin, Vector3f outMax) { + float xax = m00 * minX, xay = m01 * minX, xaz = m02 * minX; + float xbx = m00 * maxX, xby = m01 * maxX, xbz = m02 * maxX; + float yax = m10 * minY, yay = m11 * minY, yaz = m12 * minY; + float ybx = m10 * maxY, yby = m11 * maxY, ybz = m12 * maxY; + float zax = m20 * minZ, zay = m21 * minZ, zaz = m22 * minZ; + float zbx = m20 * maxZ, zby = m21 * maxZ, zbz = m22 * maxZ; + float xminx, xminy, xminz, yminx, yminy, yminz, zminx, zminy, zminz; + float xmaxx, xmaxy, xmaxz, ymaxx, ymaxy, ymaxz, zmaxx, zmaxy, zmaxz; + if (xax < xbx) { + xminx = xax; + xmaxx = xbx; + } else { + xminx = xbx; + xmaxx = xax; + } + if (xay < xby) { + xminy = xay; + xmaxy = xby; + } else { + xminy = xby; + xmaxy = xay; + } + if (xaz < xbz) { + xminz = xaz; + xmaxz = xbz; + } else { + xminz = xbz; + xmaxz = xaz; + } + if (yax < ybx) { + yminx = yax; + ymaxx = ybx; + } else { + yminx = ybx; + ymaxx = yax; + } + if (yay < yby) { + yminy = yay; + ymaxy = yby; + } else { + yminy = yby; + ymaxy = yay; + } + if (yaz < ybz) { + yminz = yaz; + ymaxz = ybz; + } else { + yminz = ybz; + ymaxz = yaz; + } + if (zax < zbx) { + zminx = zax; + zmaxx = zbx; + } else { + zminx = zbx; + zmaxx = zax; + } + if (zay < zby) { + zminy = zay; + zmaxy = zby; + } else { + zminy = zby; + zmaxy = zay; + } + if (zaz < zbz) { + zminz = zaz; + zmaxz = zbz; + } else { + zminz = zbz; + zmaxz = zaz; + } + outMin.x = xminx + yminx + zminx + m30; + outMin.y = xminy + yminy + zminy + m31; + outMin.z = xminz + yminz + zminz + m32; + outMax.x = xmaxx + ymaxx + zmaxx + m30; + outMax.y = xmaxy + ymaxy + zmaxy + m31; + outMax.z = xmaxz + ymaxz + zmaxz + m32; + return this; + } + + public Matrix4f transformAab(Vector3fc min, Vector3fc max, Vector3f outMin, Vector3f outMax) { + return transformAab(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), outMin, outMax); + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Matrix4f lerp(Matrix4fc other, float t) { + return lerp(other, t, this); + } + + public Matrix4f lerp(Matrix4fc other, float t, Matrix4f dest) { + dest._m00(Math.fma(other.m00() - m00, t, m00)) + ._m01(Math.fma(other.m01() - m01, t, m01)) + ._m02(Math.fma(other.m02() - m02, t, m02)) + ._m03(Math.fma(other.m03() - m03, t, m03)) + ._m10(Math.fma(other.m10() - m10, t, m10)) + ._m11(Math.fma(other.m11() - m11, t, m11)) + ._m12(Math.fma(other.m12() - m12, t, m12)) + ._m13(Math.fma(other.m13() - m13, t, m13)) + ._m20(Math.fma(other.m20() - m20, t, m20)) + ._m21(Math.fma(other.m21() - m21, t, m21)) + ._m22(Math.fma(other.m22() - m22, t, m22)) + ._m23(Math.fma(other.m23() - m23, t, m23)) + ._m30(Math.fma(other.m30() - m30, t, m30)) + ._m31(Math.fma(other.m31() - m31, t, m31)) + ._m32(Math.fma(other.m32() - m32, t, m32)) + ._m33(Math.fma(other.m33() - m33, t, m33)) + ._properties(properties & other.properties()); + return dest; + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3fc, Vector3fc) rotationTowards()}. + *

+ * This method is equivalent to calling: mulAffine(new Matrix4f().lookAt(new Vector3f(), new Vector3f(dir).negate(), up).invertAffine(), dest) + * + * @see #rotateTowards(float, float, float, float, float, float, Matrix4f) + * @see #rotationTowards(Vector3fc, Vector3fc) + * + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateTowards(Vector3fc dir, Vector3fc up, Matrix4f dest) { + return rotateTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3fc, Vector3fc) rotationTowards()}. + *

+ * This method is equivalent to calling: mulAffine(new Matrix4f().lookAt(new Vector3f(), new Vector3f(dir).negate(), up).invertAffine()) + * + * @see #rotateTowards(float, float, float, float, float, float) + * @see #rotationTowards(Vector3fc, Vector3fc) + * + * @param dir + * the direction to orient towards + * @param up + * the up vector + * @return this + */ + public Matrix4f rotateTowards(Vector3fc dir, Vector3fc up) { + return rotateTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ). + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(float, float, float, float, float, float) rotationTowards()}. + *

+ * This method is equivalent to calling: mulAffine(new Matrix4f().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine()) + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + return rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ) + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(float, float, float, float, float, float) rotationTowards()}. + *

+ * This method is equivalent to calling: mulAffine(new Matrix4f().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine(), dest) + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4f dest) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float ndirX = dirX * invDirLength; + float ndirY = dirY * invDirLength; + float ndirZ = dirZ * invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = ndirY * leftZ - ndirZ * leftY; + float upnY = ndirZ * leftX - ndirX * leftZ; + float upnZ = ndirX * leftY - ndirY * leftX; + float rm00 = leftX; + float rm01 = leftY; + float rm02 = leftZ; + float rm10 = upnX; + float rm11 = upnY; + float rm12 = upnZ; + float rm20 = ndirX; + float rm21 = ndirY; + float rm22 = ndirZ; + dest._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33); + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + float nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12; + dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m03(nm03) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m13(nm13) + ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with dir. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(float, float, float, float, float, float) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAt(new Vector3f(), new Vector3f(dir).negate(), up).invertAffine() + * + * @see #rotationTowards(Vector3fc, Vector3fc) + * @see #rotateTowards(float, float, float, float, float, float) + * + * @param dir + * the direction to orient the local -z axis towards + * @param up + * the up vector + * @return this + */ + public Matrix4f rotationTowards(Vector3fc dir, Vector3fc up) { + return rotationTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with (dirX, dirY, dirZ). + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(float, float, float, float, float, float) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine() + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f rotationTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float ndirX = dirX * invDirLength; + float ndirY = dirY * invDirLength; + float ndirZ = dirZ * invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = ndirY * leftZ - ndirZ * leftY; + float upnY = ndirZ * leftX - ndirX * leftZ; + float upnZ = ndirX * leftY - ndirY * leftX; + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + this._m00(leftX) + ._m01(leftY) + ._m02(leftZ) + ._m10(upnX) + ._m11(upnY) + ._m12(upnZ) + ._m20(ndirX) + ._m21(ndirY) + ._m22(ndirZ) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that translates to the given pos and aligns the local -z + * axis with dir. + *

+ * This method is equivalent to calling: translation(pos).rotateTowards(dir, up) + * + * @see #translation(Vector3fc) + * @see #rotateTowards(Vector3fc, Vector3fc) + * + * @param pos + * the position to translate to + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @return this + */ + public Matrix4f translationRotateTowards(Vector3fc pos, Vector3fc dir, Vector3fc up) { + return translationRotateTowards(pos.x(), pos.y(), pos.z(), dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that translates to the given (posX, posY, posZ) and aligns the local -z + * axis with (dirX, dirY, dirZ). + *

+ * This method is equivalent to calling: translation(posX, posY, posZ).rotateTowards(dirX, dirY, dirZ, upX, upY, upZ) + * + * @see #translation(float, float, float) + * @see #rotateTowards(float, float, float, float, float, float) + * + * @param posX + * the x-coordinate of the position to translate to + * @param posY + * the y-coordinate of the position to translate to + * @param posZ + * the z-coordinate of the position to translate to + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4f translationRotateTowards(float posX, float posY, float posZ, float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float ndirX = dirX * invDirLength; + float ndirY = dirY * invDirLength; + float ndirZ = dirZ * invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = ndirY * leftZ - ndirZ * leftY; + float upnY = ndirZ * leftX - ndirX * leftZ; + float upnZ = ndirX * leftY - ndirY * leftX; + this._m00(leftX) + ._m01(leftY) + ._m02(leftZ) + ._m03(0.0f) + ._m10(upnX) + ._m11(upnY) + ._m12(upnZ) + ._m13(0.0f) + ._m20(ndirX) + ._m21(ndirY) + ._m22(ndirZ) + ._m23(0.0f) + ._m30(posX) + ._m31(posY) + ._m32(posZ) + ._m33(1.0f) + ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL); + return this; + } + + public Vector3f getEulerAnglesZYX(Vector3f dest) { + dest.x = Math.atan2(m12, m22); + dest.y = Math.atan2(-m02, Math.sqrt(1.0f - m02 * m02)); + dest.z = Math.atan2(m01, m00); + return dest; + } + + public Vector3f getEulerAnglesXYZ(Vector3f dest) { + dest.x = Math.atan2(-m21, m22); + dest.y = Math.atan2(m20, Math.sqrt(1.0f - m20 * m20)); + dest.z = Math.atan2(-m10, m00); + return dest; + } + + /** + * Compute the extents of the coordinate system before this {@link #isAffine() affine} transformation was applied + * and store the resulting corner coordinates in corner and the span vectors in + * xDir, yDir and zDir. + *

+ * That means, given the maximum extents of the coordinate system between [-1..+1] in all dimensions, + * this method returns one corner and the length and direction of the three base axis vectors in the coordinate + * system before this transformation is applied, which transforms into the corner coordinates [-1, +1]. + *

+ * This method is equivalent to computing at least three adjacent corners using {@link #frustumCorner(int, Vector3f)} + * and subtracting them to obtain the length and direction of the span vectors. + * + * @param corner + * will hold one corner of the span (usually the corner {@link Matrix4fc#CORNER_NXNYNZ}) + * @param xDir + * will hold the direction and length of the span along the positive X axis + * @param yDir + * will hold the direction and length of the span along the positive Y axis + * @param zDir + * will hold the direction and length of the span along the positive z axis + * @return this + */ + public Matrix4f affineSpan(Vector3f corner, Vector3f xDir, Vector3f yDir, Vector3f zDir) { + float a = m10 * m22, b = m10 * m21, c = m10 * m02, d = m10 * m01; + float e = m11 * m22, f = m11 * m20, g = m11 * m02, h = m11 * m00; + float i = m12 * m21, j = m12 * m20, k = m12 * m01, l = m12 * m00; + float m = m20 * m02, n = m20 * m01, o = m21 * m02, p = m21 * m00; + float q = m22 * m01, r = m22 * m00; + float s = 1.0f / (m00 * m11 - m01 * m10) * m22 + (m02 * m10 - m00 * m12) * m21 + (m01 * m12 - m02 * m11) * m20; + float nm00 = (e - i) * s, nm01 = (o - q) * s, nm02 = (k - g) * s; + float nm10 = (j - a) * s, nm11 = (r - m) * s, nm12 = (c - l) * s; + float nm20 = (b - f) * s, nm21 = (n - p) * s, nm22 = (h - d) * s; + corner.x = -nm00 - nm10 - nm20 + (a * m31 - b * m32 + f * m32 - e * m30 + i * m30 - j * m31) * s; + corner.y = -nm01 - nm11 - nm21 + (m * m31 - n * m32 + p * m32 - o * m30 + q * m30 - r * m31) * s; + corner.z = -nm02 - nm12 - nm22 + (g * m30 - k * m30 + l * m31 - c * m31 + d * m32 - h * m32) * s; + xDir.x = 2.0f * nm00; xDir.y = 2.0f * nm01; xDir.z = 2.0f * nm02; + yDir.x = 2.0f * nm10; yDir.y = 2.0f * nm11; yDir.z = 2.0f * nm12; + zDir.x = 2.0f * nm20; zDir.y = 2.0f * nm21; zDir.z = 2.0f * nm22; + return this; + } + + public boolean testPoint(float x, float y, float z) { + float nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30; + float pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30; + float nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31; + float pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31; + float nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32; + float pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32; + return nxX * x + nxY * y + nxZ * z + nxW >= 0 && pxX * x + pxY * y + pxZ * z + pxW >= 0 && + nyX * x + nyY * y + nyZ * z + nyW >= 0 && pyX * x + pyY * y + pyZ * z + pyW >= 0 && + nzX * x + nzY * y + nzZ * z + nzW >= 0 && pzX * x + pzY * y + pzZ * z + pzW >= 0; + } + + public boolean testSphere(float x, float y, float z, float r) { + float invl; + float nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30; + invl = Math.invsqrt(nxX * nxX + nxY * nxY + nxZ * nxZ); + nxX *= invl; nxY *= invl; nxZ *= invl; nxW *= invl; + float pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30; + invl = Math.invsqrt(pxX * pxX + pxY * pxY + pxZ * pxZ); + pxX *= invl; pxY *= invl; pxZ *= invl; pxW *= invl; + float nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31; + invl = Math.invsqrt(nyX * nyX + nyY * nyY + nyZ * nyZ); + nyX *= invl; nyY *= invl; nyZ *= invl; nyW *= invl; + float pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31; + invl = Math.invsqrt(pyX * pyX + pyY * pyY + pyZ * pyZ); + pyX *= invl; pyY *= invl; pyZ *= invl; pyW *= invl; + float nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32; + invl = Math.invsqrt(nzX * nzX + nzY * nzY + nzZ * nzZ); + nzX *= invl; nzY *= invl; nzZ *= invl; nzW *= invl; + float pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32; + invl = Math.invsqrt(pzX * pzX + pzY * pzY + pzZ * pzZ); + pzX *= invl; pzY *= invl; pzZ *= invl; pzW *= invl; + return nxX * x + nxY * y + nxZ * z + nxW >= -r && pxX * x + pxY * y + pxZ * z + pxW >= -r && + nyX * x + nyY * y + nyZ * z + nyW >= -r && pyX * x + pyY * y + pyZ * z + pyW >= -r && + nzX * x + nzY * y + nzZ * z + nzW >= -r && pzX * x + pzY * y + pzZ * z + pzW >= -r; + } + + public boolean testAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + float nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30; + float pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30; + float nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31; + float pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31; + float nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32; + float pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32; + /* + * This is an implementation of the "2.4 Basic intersection test" of the mentioned site. + * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted. + */ + return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW && + pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW && + nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW && + pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW && + nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW && + pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @return this + */ + public Matrix4f obliqueZ(float a, float b) { + this.m20 = m00 * a + m10 * b + m20; + this.m21 = m01 * a + m11 * b + m21; + this.m22 = m02 * a + m12 * b + m22; + this._properties(this.properties & PROPERTY_AFFINE); + return this; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + public Matrix4f obliqueZ(float a, float b, Matrix4f dest) { + dest._m00(m00) + ._m01(m01) + ._m02(m02) + ._m03(m03) + ._m10(m10) + ._m11(m11) + ._m12(m12) + ._m13(m13) + ._m20(m00 * a + m10 * b + m20) + ._m21(m01 * a + m11 * b + m21) + ._m22(m02 * a + m12 * b + m22) + ._m23(m23) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._m33(m33) + ._properties(this.properties & PROPERTY_AFFINE); + return dest; + } + + /** + * Create a view and projection matrix from a given eye position, a given bottom left corner position p of the near plane rectangle + * and the extents of the near plane rectangle along its local x and y axes, and store the resulting matrices + * in projDest and viewDest. + *

+ * This method creates a view and perspective projection matrix assuming that there is a pinhole camera at position eye + * projecting the scene onto the near plane defined by the rectangle. + *

+ * All positions and lengths are in the same (world) unit. + * + * @param eye + * the position of the camera + * @param p + * the bottom left corner of the near plane rectangle (will map to the bottom left corner in window coordinates) + * @param x + * the direction and length of the local "bottom/top" X axis/side of the near plane rectangle + * @param y + * the direction and length of the local "left/right" Y axis/side of the near plane rectangle + * @param nearFarDist + * the distance between the far and near plane (the near plane will be calculated by this method). + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * If the special value {@link Float#NEGATIVE_INFINITY} is used, the near and far planes will be swapped and + * the near clipping plane will be at positive infinity. + * If a negative value is used (except for {@link Float#NEGATIVE_INFINITY}) the near and far planes will be swapped + * @param zeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param projDest + * will hold the resulting projection matrix + * @param viewDest + * will hold the resulting view matrix + */ + public static void projViewFromRectangle( + Vector3f eye, Vector3f p, Vector3f x, Vector3f y, float nearFarDist, boolean zeroToOne, + Matrix4f projDest, Matrix4f viewDest) { + float zx = y.y * x.z - y.z * x.y, zy = y.z * x.x - y.x * x.z, zz = y.x * x.y - y.y * x.x; + float zd = zx * (p.x - eye.x) + zy * (p.y - eye.y) + zz * (p.z - eye.z); + float zs = zd >= 0 ? 1 : -1; zx *= zs; zy *= zs; zz *= zs; zd *= zs; + viewDest.setLookAt(eye.x, eye.y, eye.z, eye.x + zx, eye.y + zy, eye.z + zz, y.x, y.y, y.z); + float px = viewDest.m00 * p.x + viewDest.m10 * p.y + viewDest.m20 * p.z + viewDest.m30; + float py = viewDest.m01 * p.x + viewDest.m11 * p.y + viewDest.m21 * p.z + viewDest.m31; + float tx = viewDest.m00 * x.x + viewDest.m10 * x.y + viewDest.m20 * x.z; + float ty = viewDest.m01 * y.x + viewDest.m11 * y.y + viewDest.m21 * y.z; + float len = Math.sqrt(zx * zx + zy * zy + zz * zz); + float near = zd / len, far; + if (Float.isInfinite(nearFarDist) && nearFarDist < 0.0f) { + far = near; + near = Float.POSITIVE_INFINITY; + } else if (Float.isInfinite(nearFarDist) && nearFarDist > 0.0f) { + far = Float.POSITIVE_INFINITY; + } else if (nearFarDist < 0.0f) { + far = near; + near = near + nearFarDist; + } else { + far = near + nearFarDist; + } + projDest.setFrustum(px, px + tx, py, py + ty, near, far, zeroToOne); + } + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3f)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3f)}) and the + * given vector up. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from + * {@link #setLookAt(Vector3fc, Vector3fc, Vector3fc)} called with the current + * local origin of this matrix (as obtained by {@link #originAffine(Vector3f)}), the sum of this position and the + * negated local Z axis as well as the given vector up. + *

+ * This method must only be called on {@link #isAffine()} matrices. + * + * @param up + * the up vector + * @return this + */ + public Matrix4f withLookAtUp(Vector3fc up) { + return withLookAtUp(up.x(), up.y(), up.z(), this); + } + + public Matrix4f withLookAtUp(Vector3fc up, Matrix4f dest) { + return withLookAtUp(up.x(), up.y(), up.z()); + } + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3f)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3f)}) and the + * given vector (upX, upY, upZ). + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from + * {@link #setLookAt(float, float, float, float, float, float, float, float, float)} called with the current + * local origin of this matrix (as obtained by {@link #originAffine(Vector3f)}), the sum of this position and the + * negated local Z axis as well as the given vector (upX, upY, upZ). + *

+ * This method must only be called on {@link #isAffine()} matrices. + * + * @param upX + * the x coordinate of the up vector + * @param upY + * the y coordinate of the up vector + * @param upZ + * the z coordinate of the up vector + * @return this + */ + public Matrix4f withLookAtUp(float upX, float upY, float upZ) { + return withLookAtUp(upX, upY, upZ, this); + } + + public Matrix4f withLookAtUp(float upX, float upY, float upZ, Matrix4f dest) { + float y = (upY * m21 - upZ * m11) * m02 + + (upZ * m01 - upX * m21) * m12 + + (upX * m11 - upY * m01) * m22; + float x = upX * m01 + upY * m11 + upZ * m21; + if ((properties & PROPERTY_ORTHONORMAL) == 0) + x *= Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21); + float invsqrt = Math.invsqrt(y * y + x * x); + float c = x * invsqrt, s = y * invsqrt; + float nm00 = c * m00 - s * m01, nm10 = c * m10 - s * m11, nm20 = c * m20 - s * m21, nm31 = s * m30 + c * m31; + float nm01 = s * m00 + c * m01, nm11 = s * m10 + c * m11, nm21 = s * m20 + c * m21, nm30 = c * m30 - s * m31; + dest._m00(nm00)._m10(nm10)._m20(nm20)._m30(nm30) + ._m01(nm01)._m11(nm11)._m21(nm21)._m31(nm31); + if (dest != this) { + dest._m02(m02)._m12(m12)._m22(m22)._m32(m32) + ._m03(m03)._m13(m13)._m23(m23)._m33(m33); + } + dest._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Multiply this by the matrix + *

+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 1 0 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapXZY() { + return mapXZY(this); + } + public Matrix4f mapXZY(Matrix4f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapXZnY() { + return mapXZnY(this); + } + public Matrix4f mapXZnY(Matrix4f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapXnYnZ() { + return mapXnYnZ(this); + } + public Matrix4f mapXnYnZ(Matrix4f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapXnZY() { + return mapXnZY(this); + } + public Matrix4f mapXnZY(Matrix4f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapXnZnY() { + return mapXnZnY(this); + } + public Matrix4f mapXnZnY(Matrix4f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapYXZ() { + return mapYXZ(this); + } + public Matrix4f mapYXZ(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapYXnZ() { + return mapYXnZ(this); + } + public Matrix4f mapYXnZ(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 1 0 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapYZX() { + return mapYZX(this); + } + public Matrix4f mapYZX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapYZnX() { + return mapYZnX(this); + } + public Matrix4f mapYZnX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapYnXZ() { + return mapYnXZ(this); + } + public Matrix4f mapYnXZ(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapYnXnZ() { + return mapYnXnZ(this); + } + public Matrix4f mapYnXnZ(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapYnZX() { + return mapYnZX(this); + } + public Matrix4f mapYnZX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapYnZnX() { + return mapYnZnX(this); + } + public Matrix4f mapYnZnX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapZXY() { + return mapZXY(this); + } + public Matrix4f mapZXY(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapZXnY() { + return mapZXnY(this); + } + public Matrix4f mapZXnY(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapZYX() { + return mapZYX(this); + } + public Matrix4f mapZYX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapZYnX() { + return mapZYnX(this); + } + public Matrix4f mapZYnX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapZnXY() { + return mapZnXY(this); + } + public Matrix4f mapZnXY(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapZnXnY() { + return mapZnXnY(this); + } + public Matrix4f mapZnXnY(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapZnYX() { + return mapZnYX(this); + } + public Matrix4f mapZnYX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapZnYnX() { + return mapZnYnX(this); + } + public Matrix4f mapZnYnX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0 -1 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnXYnZ() { + return mapnXYnZ(this); + } + public Matrix4f mapnXYnZ(Matrix4f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 1 0 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnXZY() { + return mapnXZY(this); + } + public Matrix4f mapnXZY(Matrix4f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 1  0 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnXZnY() { + return mapnXZnY(this); + } + public Matrix4f mapnXZnY(Matrix4f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 1 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnXnYZ() { + return mapnXnYZ(this); + } + public Matrix4f mapnXnYZ(Matrix4f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0 -1 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnXnYnZ() { + return mapnXnYnZ(this); + } + public Matrix4f mapnXnYnZ(Matrix4f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0 -1 0 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnXnZY() { + return mapnXnZY(this); + } + public Matrix4f mapnXnZY(Matrix4f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0 -1  0 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnXnZnY() { + return mapnXnZnY(this); + } + public Matrix4f mapnXnZnY(Matrix4f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnYXZ() { + return mapnYXZ(this); + } + public Matrix4f mapnYXZ(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnYXnZ() { + return mapnYXnZ(this); + } + public Matrix4f mapnYXnZ(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnYZX() { + return mapnYZX(this); + } + public Matrix4f mapnYZX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnYZnX() { + return mapnYZnX(this); + } + public Matrix4f mapnYZnX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnYnXZ() { + return mapnYnXZ(this); + } + public Matrix4f mapnYnXZ(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnYnXnZ() { + return mapnYnXnZ(this); + } + public Matrix4f mapnYnXnZ(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnYnZX() { + return mapnYnZX(this); + } + public Matrix4f mapnYnZX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnYnZnX() { + return mapnYnZnX(this); + } + public Matrix4f mapnYnZnX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnZXY() { + return mapnZXY(this); + } + public Matrix4f mapnZXY(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnZXnY() { + return mapnZXnY(this); + } + public Matrix4f mapnZXnY(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnZYX() { + return mapnZYX(this); + } + public Matrix4f mapnZYX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnZYnX() { + return mapnZYnX(this); + } + public Matrix4f mapnZYnX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnZnXY() { + return mapnZnXY(this); + } + public Matrix4f mapnZnXY(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnZnXnY() { + return mapnZnXnY(this); + } + public Matrix4f mapnZnXnY(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnZnYX() { + return mapnZnYX(this); + } + public Matrix4f mapnZnYX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f mapnZnYnX() { + return mapnZnYnX(this); + } + public Matrix4f mapnZnYnX(Matrix4f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL)); + } + + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 1 0
+     *  0 0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f negateX() { + return _m00(-m00)._m01(-m01)._m02(-m02); + } + public Matrix4f negateX(Matrix4f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33); + } + + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 0  0 0 1
+     * 
+ * + * @return this + */ + public Matrix4f negateY() { + return _m10(-m10)._m11(-m11)._m12(-m12); + } + public Matrix4f negateY(Matrix4f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33); + } + + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 0 0  0 1
+     * 
+ * + * @return this + */ + public Matrix4f negateZ() { + return _m20(-m20)._m21(-m21)._m22(-m22); + } + public Matrix4f negateZ(Matrix4f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33); + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && Math.isFinite(m03) && + Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && Math.isFinite(m13) && + Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22) && Math.isFinite(m23) && + Math.isFinite(m30) && Math.isFinite(m31) && Math.isFinite(m32) && Math.isFinite(m33); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4fStack.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4fStack.java new file mode 100644 index 000000000..c768bd4b4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4fStack.java @@ -0,0 +1,185 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A stack of many {@link Matrix4f} instances. This resembles the matrix stack known from legacy OpenGL. + *

+ * This {@link Matrix4fStack} class inherits from {@link Matrix4f}, so the current/top matrix is always the {@link Matrix4fStack}/{@link Matrix4f} itself. This + * affects all operations in {@link Matrix4f} that take another {@link Matrix4f} as parameter. If a {@link Matrix4fStack} is used as argument to those methods, + * the effective argument will always be the current matrix of the matrix stack. + * + * @author Kai Burjack + */ +public class Matrix4fStack extends Matrix4f { + + private static final long serialVersionUID = 1L; + + /** + * The matrix stack as a non-growable array. The size of the stack must be specified in the {@link #Matrix4fStack(int) constructor}. + */ + private Matrix4f[] mats; + + /** + * The index of the "current" matrix within {@link #mats}. + */ + private int curr; + + /** + * Create a new {@link Matrix4fStack} of the given size. + *

+ * Initially the stack pointer is at zero and the current matrix is set to identity. + * + * @param stackSize + * the size of the stack. This must be at least 1, in which case the {@link Matrix4fStack} simply only consists of this + * {@link Matrix4f} + */ + public Matrix4fStack(int stackSize) { + if (stackSize < 1) { + throw new IllegalArgumentException("stackSize must be >= 1"); //$NON-NLS-1$ + } + mats = new Matrix4f[stackSize - 1]; + // Allocate all matrices up front to keep the promise of being "allocation-free" + for (int i = 0; i < mats.length; i++) { + mats[i] = new Matrix4f(); + } + } + + /** + * Do not invoke manually! Only meant for serialization. + *

+ * Invoking this constructor from client code will result in an inconsistent state of the + * created {@link Matrix4fStack} instance. + */ + public Matrix4fStack() { + /* Empty! */ + } + + /** + * Set the stack pointer to zero and set the current/bottom matrix to {@link #identity() identity}. + * + * @return this + */ + public Matrix4fStack clear() { + curr = 0; + identity(); + return this; + } + + /** + * Increment the stack pointer by one and set the values of the new current matrix to the one directly below it. + * + * @return this + */ + public Matrix4fStack pushMatrix() { + if (curr == mats.length) { + throw new IllegalStateException("max stack size of " + (curr + 1) + " reached"); //$NON-NLS-1$ //$NON-NLS-2$ + } + mats[curr++].set(this); + return this; + } + + /** + * Decrement the stack pointer by one. + *

+ * This will effectively dispose of the current matrix. + * + * @return this + */ + public Matrix4fStack popMatrix() { + if (curr == 0) { + throw new IllegalStateException("already at the bottom of the stack"); //$NON-NLS-1$ + } + set(mats[--curr]); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + curr; + for (int i = 0; i < curr; i++) { + result = prime * result + mats[i].hashCode(); + } + return result; + } + + /* + * Contract between Matrix4f and Matrix4fStack: + * + * - Matrix4f.equals(Matrix4fStack) is true iff all the 16 matrix elements are equal + * - Matrix4fStack.equals(Matrix4f) is true iff all the 16 matrix elements are equal + * - Matrix4fStack.equals(Matrix4fStack) is true iff all 16 matrix elements are equal AND the matrix arrays as well as the stack pointer are equal + * - everything else is inequal + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (obj instanceof Matrix4fStack) { + Matrix4fStack other = (Matrix4fStack) obj; + if (curr != other.curr) + return false; + for (int i = 0; i < curr; i++) { + if (!mats[i].equals(other.mats[i])) + return false; + } + } + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(curr); + for (int i = 0; i < curr; i++) { + out.writeObject(mats[i]); + } + } + + public void readExternal(ObjectInput in) throws IOException { + super.readExternal(in); + curr = in.readInt(); + mats = new Matrix4fStack[curr]; + for (int i = 0; i < curr; i++) { + Matrix4f m = new Matrix4f(); + m.readExternal(in); + mats[i] = m; + } + } + + public Object clone() throws CloneNotSupportedException { + Matrix4fStack cloned = (Matrix4fStack) super.clone(); + Matrix4f[] clonedMats = new Matrix4f[mats.length]; + for (int i = 0; i < mats.length; i++) + clonedMats[i] = (Matrix4f) mats[i].clone(); + cloned.mats = clonedMats; + return cloned; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4fc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4fc.java new file mode 100644 index 000000000..8b32de51f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4fc.java @@ -0,0 +1,6082 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + + +/** + * Interface to a read-only view of a 4x4 matrix of single-precision floats. + * + * @author Kai Burjack + */ +public interface Matrix4fc { + + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation x=-1 when using the identity matrix. + */ + int PLANE_NX = 0; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation x=1 when using the identity matrix. + */ + int PLANE_PX = 1; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation y=-1 when using the identity matrix. + */ + int PLANE_NY = 2; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation y=1 when using the identity matrix. + */ + int PLANE_PY = 3; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation z=-1 when using the identity matrix. + */ + int PLANE_NZ = 4; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation z=1 when using the identity matrix. + */ + int PLANE_PZ = 5; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3f)} + * identifying the corner (-1, -1, -1) when using the identity matrix. + */ + int CORNER_NXNYNZ = 0; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3f)} + * identifying the corner (1, -1, -1) when using the identity matrix. + */ + int CORNER_PXNYNZ = 1; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3f)} + * identifying the corner (1, 1, -1) when using the identity matrix. + */ + int CORNER_PXPYNZ = 2; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3f)} + * identifying the corner (-1, 1, -1) when using the identity matrix. + */ + int CORNER_NXPYNZ = 3; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3f)} + * identifying the corner (1, -1, 1) when using the identity matrix. + */ + int CORNER_PXNYPZ = 4; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3f)} + * identifying the corner (-1, -1, 1) when using the identity matrix. + */ + int CORNER_NXNYPZ = 5; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3f)} + * identifying the corner (-1, 1, 1) when using the identity matrix. + */ + int CORNER_NXPYPZ = 6; + /** + * Argument to the first parameter of {@link #frustumCorner(int, Vector3f)} + * identifying the corner (1, 1, 1) when using the identity matrix. + */ + int CORNER_PXPYPZ = 7; + + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents a perspective transformation. + */ + byte PROPERTY_PERSPECTIVE = 1<<0; + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents an affine transformation. + */ + byte PROPERTY_AFFINE = 1<<1; + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents the identity transformation. + */ + byte PROPERTY_IDENTITY = 1<<2; + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents a pure translation transformation. + */ + byte PROPERTY_TRANSLATION = 1<<3; + /** + * Bit returned by {@link #properties()} to indicate that the upper-left 3x3 submatrix represents an orthogonal + * matrix (i.e. orthonormal basis). For practical reasons, this property also always implies + * {@link #PROPERTY_AFFINE} in this implementation. + */ + byte PROPERTY_ORTHONORMAL = 1<<4; + + /** + * Return the assumed properties of this matrix. This is a bit-combination of + * {@link #PROPERTY_IDENTITY}, {@link #PROPERTY_AFFINE}, + * {@link #PROPERTY_TRANSLATION} and {@link #PROPERTY_PERSPECTIVE}. + * + * @return the properties of the matrix + */ + int properties(); + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + float m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + float m01(); + + /** + * Return the value of the matrix element at column 0 and row 2. + * + * @return the value of the matrix element + */ + float m02(); + + /** + * Return the value of the matrix element at column 0 and row 3. + * + * @return the value of the matrix element + */ + float m03(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + float m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + float m11(); + + /** + * Return the value of the matrix element at column 1 and row 2. + * + * @return the value of the matrix element + */ + float m12(); + + /** + * Return the value of the matrix element at column 1 and row 3. + * + * @return the value of the matrix element + */ + float m13(); + + /** + * Return the value of the matrix element at column 2 and row 0. + * + * @return the value of the matrix element + */ + float m20(); + + /** + * Return the value of the matrix element at column 2 and row 1. + * + * @return the value of the matrix element + */ + float m21(); + + /** + * Return the value of the matrix element at column 2 and row 2. + * + * @return the value of the matrix element + */ + float m22(); + + /** + * Return the value of the matrix element at column 2 and row 3. + * + * @return the value of the matrix element + */ + float m23(); + + /** + * Return the value of the matrix element at column 3 and row 0. + * + * @return the value of the matrix element + */ + float m30(); + + /** + * Return the value of the matrix element at column 3 and row 1. + * + * @return the value of the matrix element + */ + float m31(); + + /** + * Return the value of the matrix element at column 3 and row 2. + * + * @return the value of the matrix element + */ + float m32(); + + /** + * Return the value of the matrix element at column 3 and row 3. + * + * @return the value of the matrix element + */ + float m33(); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mul(Matrix4fc right, Matrix4f dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + *

+ * This method neither assumes nor checks for any matrix properties of this or right + * and will always perform a complete 4x4 matrix multiplication. This method should only be used whenever the + * multiplied matrices do not have any properties for which there are optimized multiplication methods available. + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mul0(Matrix4fc right, Matrix4f dest); + + /** + * Multiply this matrix by the matrix with the supplied elements and store the result in dest. + *

+ * If M is this matrix and R the right matrix whose + * elements are supplied via the parameters, then the new matrix will be M * R. + * So when transforming a vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param r00 + * the m00 element of the right matrix + * @param r01 + * the m01 element of the right matrix + * @param r02 + * the m02 element of the right matrix + * @param r03 + * the m03 element of the right matrix + * @param r10 + * the m10 element of the right matrix + * @param r11 + * the m11 element of the right matrix + * @param r12 + * the m12 element of the right matrix + * @param r13 + * the m13 element of the right matrix + * @param r20 + * the m20 element of the right matrix + * @param r21 + * the m21 element of the right matrix + * @param r22 + * the m22 element of the right matrix + * @param r23 + * the m23 element of the right matrix + * @param r30 + * the m30 element of the right matrix + * @param r31 + * the m31 element of the right matrix + * @param r32 + * the m32 element of the right matrix + * @param r33 + * the m33 element of the right matrix + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mul( + float r00, float r01, float r02, float r03, + float r10, float r11, float r12, float r13, + float r20, float r21, float r22, float r23, + float r30, float r31, float r32, float r33, Matrix4f dest); + + /** + * Multiply this matrix by the 3x3 matrix with the supplied elements expanded to a 4x4 matrix with + * all other matrix elements set to identity, and store the result in dest. + *

+ * If M is this matrix and R the right matrix whose + * elements are supplied via the parameters, then the new matrix will be M * R. + * So when transforming a vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param r00 + * the m00 element of the right matrix + * @param r01 + * the m01 element of the right matrix + * @param r02 + * the m02 element of the right matrix + * @param r10 + * the m10 element of the right matrix + * @param r11 + * the m11 element of the right matrix + * @param r12 + * the m12 element of the right matrix + * @param r20 + * the m20 element of the right matrix + * @param r21 + * the m21 element of the right matrix + * @param r22 + * the m22 element of the right matrix + * @param dest + * the destination matrix, which will hold the result + * @return this + */ + Matrix4f mul3x3( + float r00, float r01, float r02, + float r10, float r11, float r12, + float r20, float r21, float r22, Matrix4f dest); + + /** + * Pre-multiply this matrix by the supplied left matrix and store the result in dest. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mulLocal(Matrix4fc left, Matrix4f dest); + + /** + * Pre-multiply this matrix by the supplied left matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in dest. + *

+ * This method assumes that this matrix and the given left matrix both represent an {@link #isAffine() affine} transformation + * (i.e. their last rows are equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * This method will not modify either the last row of this or the last row of left. + *

+ * If M is this matrix and L the left matrix, + * then the new matrix will be L * M. So when transforming a + * vector v with the new matrix by using L * M * v, the + * transformation of this matrix will be applied first! + * + * @param left + * the left operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mulLocalAffine(Matrix4fc left, Matrix4f dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mul(Matrix3x2fc right, Matrix4f dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * The last row of the right matrix is assumed to be (0, 0, 0, 1). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mul(Matrix4x3fc right, Matrix4f dest); + + /** + * Multiply this symmetric perspective projection matrix by the supplied {@link #isAffine() affine} view matrix and store the result in dest. + *

+ * If P is this matrix and V the view matrix, + * then the new matrix will be P * V. So when transforming a + * vector v with the new matrix by using P * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the {@link #isAffine() affine} matrix to multiply this symmetric perspective projection matrix by + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mulPerspectiveAffine(Matrix4fc view, Matrix4f dest); + + /** + * Multiply this symmetric perspective projection matrix by the supplied view matrix and store the result in dest. + *

+ * If P is this matrix and V the view matrix, + * then the new matrix will be P * V. So when transforming a + * vector v with the new matrix by using P * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the matrix to multiply this symmetric perspective projection matrix by + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mulPerspectiveAffine(Matrix4x3fc view, Matrix4f dest); + + /** + * Multiply this matrix by the supplied right matrix, which is assumed to be {@link #isAffine() affine}, and store the result in dest. + *

+ * This method assumes that the given right matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mulAffineR(Matrix4fc right, Matrix4f dest); + + /** + * Multiply this matrix by the supplied right matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in dest. + *

+ * This method assumes that this matrix and the given right matrix both represent an {@link #isAffine() affine} transformation + * (i.e. their last rows are equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mulAffine(Matrix4fc right, Matrix4f dest); + + /** + * Multiply this matrix, which is assumed to only contain a translation, by the supplied right matrix, which is assumed to be {@link #isAffine() affine}, and store the result in dest. + *

+ * This method assumes that this matrix only contains a translation, and that the given right matrix represents an {@link #isAffine() affine} transformation + * (i.e. its last row is equal to (0, 0, 0, 1)). + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication (the last row is assumed to be (0, 0, 0, 1)) + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mulTranslationAffine(Matrix4fc right, Matrix4f dest); + + /** + * Multiply this orthographic projection matrix by the supplied {@link #isAffine() affine} view matrix + * and store the result in dest. + *

+ * If M is this matrix and V the view matrix, + * then the new matrix will be M * V. So when transforming a + * vector v with the new matrix by using M * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the affine matrix which to multiply this with + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f mulOrthoAffine(Matrix4fc view, Matrix4f dest); + + /** + * Component-wise add the upper 4x3 submatrices of this and other + * by first multiplying each component of other's 4x3 submatrix by otherFactor, + * adding that to this and storing the final result in dest. + *

+ * The other components of dest will be set to the ones of this. + *

+ * The matrices this and other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's 4x3 components + * @param dest + * will hold the result + * @return dest + */ + Matrix4f fma4x3(Matrix4fc other, float otherFactor, Matrix4f dest); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix4f add(Matrix4fc other, Matrix4f dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix4f sub(Matrix4fc subtrahend, Matrix4f dest); + + /** + * Component-wise multiply this by other and store the result in dest. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mulComponentWise(Matrix4fc other, Matrix4f dest); + + /** + * Component-wise add the upper 4x3 submatrices of this and other + * and store the result in dest. + *

+ * The other components of dest will be set to the ones of this. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix4f add4x3(Matrix4fc other, Matrix4f dest); + + /** + * Component-wise subtract the upper 4x3 submatrices of subtrahend from this + * and store the result in dest. + *

+ * The other components of dest will be set to the ones of this. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix4f sub4x3(Matrix4fc subtrahend, Matrix4f dest); + + /** + * Component-wise multiply the upper 4x3 submatrices of this by other + * and store the result in dest. + *

+ * The other components of dest will be set to the ones of this. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mul4x3ComponentWise(Matrix4fc other, Matrix4f dest); + + /** + * Return the determinant of this matrix. + *

+ * If this matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing, + * and thus its last row is equal to (0, 0, 0, 1), then {@link #determinantAffine()} can be used instead of this method. + * + * @see #determinantAffine() + * + * @return the determinant + */ + float determinant(); + + /** + * Return the determinant of the upper left 3x3 submatrix of this matrix. + * + * @return the determinant + */ + float determinant3x3(); + + /** + * Return the determinant of this matrix by assuming that it represents an {@link #isAffine() affine} transformation and thus + * its last row is equal to (0, 0, 0, 1). + * + * @return the determinant + */ + float determinantAffine(); + + /** + * Invert this matrix and write the result into dest. + *

+ * If this matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing, + * and thus its last row is equal to (0, 0, 0, 1), then {@link #invertAffine(Matrix4f)} can be used instead of this method. + * + * @see #invertAffine(Matrix4f) + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f invert(Matrix4f dest); + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(float, float, float, float, Matrix4f) perspective()} methods, + * that is, if this is a symmetrical perspective frustum transformation, + * then this method builds the inverse of this and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix when being obtained via {@link #perspective(float, float, float, float, Matrix4f) perspective()}. + * + * @see #perspective(float, float, float, float, Matrix4f) + * + * @param dest + * will hold the inverse of this + * @return dest + */ + Matrix4f invertPerspective(Matrix4f dest); + + /** + * If this is an arbitrary perspective projection matrix obtained via one of the {@link #frustum(float, float, float, float, float, float, Matrix4f) frustum()} methods, + * then this method builds the inverse of this and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of a perspective projection matrix. + *

+ * If this matrix represents a symmetric perspective frustum transformation, as obtained via {@link #perspective(float, float, float, float, Matrix4f) perspective()}, then + * {@link #invertPerspective(Matrix4f)} should be used instead. + * + * @see #frustum(float, float, float, float, float, float, Matrix4f) + * @see #invertPerspective(Matrix4f) + * + * @param dest + * will hold the inverse of this + * @return dest + */ + Matrix4f invertFrustum(Matrix4f dest); + + /** + * Invert this orthographic projection matrix and store the result into the given dest. + *

+ * This method can be used to quickly obtain the inverse of an orthographic projection matrix. + * + * @param dest + * will hold the inverse of this + * @return dest + */ + Matrix4f invertOrtho(Matrix4f dest); + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(float, float, float, float, Matrix4f) perspective()} methods, + * that is, if this is a symmetrical perspective frustum transformation + * and the given view matrix is {@link #isAffine() affine} and has unit scaling (for example by being obtained via {@link #lookAt(float, float, float, float, float, float, float, float, float, Matrix4f) lookAt()}), + * then this method builds the inverse of this * view and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of the combination of the view and projection matrices, when both were obtained + * via the common methods {@link #perspective(float, float, float, float, Matrix4f) perspective()} and {@link #lookAt(float, float, float, float, float, float, float, float, float, Matrix4f) lookAt()} or + * other methods, that build affine matrices, such as {@link #translate(float, float, float, Matrix4f) translate} and {@link #rotate(float, float, float, float, Matrix4f)}, except for {@link #scale(float, float, float, Matrix4f) scale()}. + *

+ * For the special cases of the matrices this and view mentioned above, this method is equivalent to the following code: + *

+     * dest.set(this).mul(view).invert();
+     * 
+ * + * @param view + * the view transformation (must be {@link #isAffine() affine} and have unit scaling) + * @param dest + * will hold the inverse of this * view + * @return dest + */ + Matrix4f invertPerspectiveView(Matrix4fc view, Matrix4f dest); + + /** + * If this is a perspective projection matrix obtained via one of the {@link #perspective(float, float, float, float, Matrix4f) perspective()} methods, + * that is, if this is a symmetrical perspective frustum transformation + * and the given view matrix has unit scaling, + * then this method builds the inverse of this * view and stores it into the given dest. + *

+ * This method can be used to quickly obtain the inverse of the combination of the view and projection matrices, when both were obtained + * via the common methods {@link #perspective(float, float, float, float, Matrix4f) perspective()} and {@link #lookAt(float, float, float, float, float, float, float, float, float, Matrix4f) lookAt()} or + * other methods, that build affine matrices, such as {@link #translate(float, float, float, Matrix4f) translate} and {@link #rotate(float, float, float, float, Matrix4f)}, except for {@link #scale(float, float, float, Matrix4f) scale()}. + *

+ * For the special cases of the matrices this and view mentioned above, this method is equivalent to the following code: + *

+     * dest.set(this).mul(view).invert();
+     * 
+ * + * @param view + * the view transformation (must have unit scaling) + * @param dest + * will hold the inverse of this * view + * @return dest + */ + Matrix4f invertPerspectiveView(Matrix4x3fc view, Matrix4f dest); + + /** + * Invert this matrix by assuming that it is an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and write the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f invertAffine(Matrix4f dest); + + /** + * Transpose this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f transpose(Matrix4f dest); + + /** + * Transpose only the upper left 3x3 submatrix of this matrix and store the result in dest. + *

+ * All other matrix elements are left unchanged. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f transpose3x3(Matrix4f dest); + + /** + * Transpose only the upper left 3x3 submatrix of this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f transpose3x3(Matrix3f dest); + + /** + * Get only the translation components (m30, m31, m32) of this matrix and store them in the given vector xyz. + * + * @param dest + * will hold the translation components of this matrix + * @return dest + */ + Vector3f getTranslation(Vector3f dest); + + /** + * Get the scaling factors of this matrix for the three base axes. + * + * @param dest + * will hold the scaling factors for x, y and z + * @return dest + */ + Vector3f getScale(Vector3f dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4f get(Matrix4f dest); + + /** + * Get the current values of the upper 4x3 submatrix of this matrix and store them into + * dest. + * + * @see Matrix4x3f#set(Matrix4fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4x3f get4x3(Matrix4x3f dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4d get(Matrix4d dest); + + /** + * Get the current values of the upper left 3x3 submatrix of this matrix and store them into + * dest. + * + * @see Matrix3f#set(Matrix4fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3f get3x3(Matrix3f dest); + + /** + * Get the current values of the upper left 3x3 submatrix of this matrix and store them into + * dest. + * + * @see Matrix3d#set(Matrix4fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix3d get3x3(Matrix3d dest); + + /** + * Get the rotational component of this matrix and store the represented rotation + * into the given {@link AxisAngle4f}. + * + * @see AxisAngle4f#set(Matrix4fc) + * + * @param dest + * the destination {@link AxisAngle4f} + * @return the passed in destination + */ + AxisAngle4f getRotation(AxisAngle4f dest); + + /** + * Get the rotational component of this matrix and store the represented rotation + * into the given {@link AxisAngle4d}. + * + * @see AxisAngle4f#set(Matrix4fc) + * + * @param dest + * the destination {@link AxisAngle4d} + * @return the passed in destination + */ + AxisAngle4d getRotation(AxisAngle4d dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the first three column vectors of the upper left 3x3 submatrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaternionf#setFromUnnormalized(Matrix4fc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getUnnormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the first three column vectors of the upper left 3x3 submatrix are normalized. + * + * @see Quaternionf#setFromNormalized(Matrix4fc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getNormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the first three column vectors of the upper left 3x3 submatrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaterniond#setFromUnnormalized(Matrix4fc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getUnnormalizedRotation(Quaterniond dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the first three column vectors of the upper left 3x3 submatrix are normalized. + * + * @see Quaterniond#setFromNormalized(Matrix4fc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getNormalizedRotation(Quaterniond dest); + + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store the upper 4x3 submatrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of the upper 4x3 submatrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get4x3(FloatBuffer buffer); + + /** + * Store the upper 4x3 submatrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of the upper 4x3 submatrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get4x3(int index, FloatBuffer buffer); + + /** + * Store the upper 4x3 submatrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of the upper 4x3 submatrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get4x3(ByteBuffer buffer); + + /** + * Store the upper 4x3 submatrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of the upper 4x3 submatrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get4x3(int index, ByteBuffer buffer); + + /** + * Store the left 3x4 submatrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get3x4(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x4(int, FloatBuffer) + * + * @param buffer + * will receive the values of the left 3x4 submatrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get3x4(FloatBuffer buffer); + + /** + * Store the left 3x4 submatrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of the left 3x4 submatrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get3x4(int index, FloatBuffer buffer); + + /** + * Store the left 3x4 submatrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get3x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of the left 3x4 submatrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get3x4(ByteBuffer buffer); + + /** + * Store the left 3x4 submatrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of the left 3x4 submatrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get3x4(int index, ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #getTransposed(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer getTransposed(FloatBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer getTransposed(int index, FloatBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getTransposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getTransposed(ByteBuffer buffer); + + /** + * Store the transpose of this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer getTransposed(int index, ByteBuffer buffer); + + /** + * Store the upper 4x3 submatrix of this matrix in row-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get4x3Transposed(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x3Transposed(int, FloatBuffer) + * + * @param buffer + * will receive the values of the upper 4x3 submatrix in row-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get4x3Transposed(FloatBuffer buffer); + + /** + * Store the upper 4x3 submatrix of this matrix in row-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of the upper 4x3 submatrix in row-major order + * @return the passed in buffer + */ + FloatBuffer get4x3Transposed(int index, FloatBuffer buffer); + + /** + * Store the upper 4x3 submatrix of this matrix in row-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get4x3Transposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x3Transposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of the upper 4x3 submatrix in row-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get4x3Transposed(ByteBuffer buffer); + + /** + * Store the upper 4x3 submatrix of this matrix in row-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of the upper 4x3 submatrix in row-major order + * @return the passed in buffer + */ + ByteBuffer get4x3Transposed(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix4fc getToAddress(long address); + + /** + * Store this matrix into the supplied float array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get(float[] arr, int offset); + + /** + * Store this matrix into the supplied float array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get(float[] arr); + + /** + * Transform/multiply the given vector by this matrix and store the result in that vector. + * + * @see Vector4f#mul(Matrix4fc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4f transform(Vector4f v); + + /** + * Transform/multiply the given vector by this matrix and store the result in dest. + * + * @see Vector4f#mul(Matrix4fc, Vector4f) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4f transform(Vector4fc v, Vector4f dest); + + /** + * Transform/multiply the vector (x, y, z, w) by this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param w + * the w coordinate of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4f transform(float x, float y, float z, float w, Vector4f dest); + + /** + * Transform/multiply the given vector by the transpose of this matrix and store the result in that vector. + * + * @see Vector4f#mulTranspose(Matrix4fc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4f transformTranspose(Vector4f v); + + /** + * Transform/multiply the given vector by the transpose of this matrix and store the result in dest. + * + * @see Vector4f#mulTranspose(Matrix4fc, Vector4f) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4f transformTranspose(Vector4fc v, Vector4f dest); + + /** + * Transform/multiply the vector (x, y, z, w) by the transpose of this matrix and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param w + * the w coordinate of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4f transformTranspose(float x, float y, float z, float w, Vector4f dest); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in that vector. + * + * @see Vector4f#mulProject(Matrix4fc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4f transformProject(Vector4f v); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in dest. + * + * @see Vector4f#mulProject(Matrix4fc, Vector4f) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4f transformProject(Vector4fc v, Vector4f dest); + + /** + * Transform/multiply the vector (x, y, z, w) by this matrix, perform perspective divide and store the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param w + * the w coordinate of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4f transformProject(float x, float y, float z, float w, Vector4f dest); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in that vector. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @see Vector3f#mulProject(Matrix4fc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3f transformProject(Vector3f v); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in dest. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @see Vector3f#mulProject(Matrix4fc, Vector3f) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3f transformProject(Vector3fc v, Vector3f dest); + + /** + * Transform/multiply the given vector by this matrix, perform perspective divide and store the result in dest. + * + * @see Vector4f#mulProject(Matrix4fc, Vector4f) + * + * @param v + * the vector to transform + * @param dest + * will contain the (x, y, z) components of the result + * @return dest + */ + Vector3f transformProject(Vector4fc v, Vector3f dest); + + /** + * Transform/multiply the vector (x, y, z) by this matrix, perform perspective divide and store the result in dest. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector3f transformProject(float x, float y, float z, Vector3f dest); + + /** + * Transform/multiply the vector (x, y, z, w) by this matrix, perform perspective divide and store + * (x, y, z) of the result in dest. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param w + * the w coordinate of the vector to transform + * @param dest + * will contain the (x, y, z) components of the result + * @return dest + */ + Vector3f transformProject(float x, float y, float z, float w, Vector3f dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=1, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. This method is therefore + * not suited for perspective projection transformations as it will not save the + * w component of the transformed vector. + * For perspective projection use {@link #transform(Vector4f)} or {@link #transformProject(Vector3f)} + * when perspective divide should be applied, too. + *

+ * In order to store the result in another vector, use {@link #transformPosition(Vector3fc, Vector3f)}. + * + * @see #transformPosition(Vector3fc, Vector3f) + * @see #transform(Vector4f) + * @see #transformProject(Vector3f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3f transformPosition(Vector3f v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=1, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. This method is therefore + * not suited for perspective projection transformations as it will not save the + * w component of the transformed vector. + * For perspective projection use {@link #transform(Vector4fc, Vector4f)} or + * {@link #transformProject(Vector3fc, Vector3f)} when perspective divide should be applied, too. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector3f)}. + * + * @see #transformPosition(Vector3f) + * @see #transform(Vector4fc, Vector4f) + * @see #transformProject(Vector3fc, Vector3f) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPosition(Vector3fc v, Vector3f dest); + + /** + * Transform/multiply the 3D-vector (x, y, z), as if it was a 4D-vector with w=1, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. This method is therefore + * not suited for perspective projection transformations as it will not save the + * w component of the transformed vector. + * For perspective projection use {@link #transform(float, float, float, float, Vector4f)} or + * {@link #transformProject(float, float, float, Vector3f)} when perspective divide should be applied, too. + * + * @see #transform(float, float, float, float, Vector4f) + * @see #transformProject(float, float, float, Vector3f) + * + * @param x + * the x coordinate of the position + * @param y + * the y coordinate of the position + * @param z + * the z coordinate of the position + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPosition(float x, float y, float z, Vector3f dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector3fc, Vector3f)}. + * + * @see #transformDirection(Vector3fc, Vector3f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3f transformDirection(Vector3f v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector3f)}. + * + * @see #transformDirection(Vector3f) + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformDirection(Vector3fc v, Vector3f dest); + + /** + * Transform/multiply the given 3D-vector (x, y, z), as if it was a 4D-vector with w=0, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + * + * @param x + * the x coordinate of the direction to transform + * @param y + * the y coordinate of the direction to transform + * @param z + * the z coordinate of the direction to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformDirection(float x, float y, float z, Vector3f dest); + + /** + * Transform/multiply the given 4D-vector by assuming that this matrix represents an {@link #isAffine() affine} transformation + * (i.e. its last row is equal to (0, 0, 0, 1)). + *

+ * In order to store the result in another vector, use {@link #transformAffine(Vector4fc, Vector4f)}. + * + * @see #transformAffine(Vector4fc, Vector4f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4f transformAffine(Vector4f v); + + /** + * Transform/multiply the given 4D-vector by assuming that this matrix represents an {@link #isAffine() affine} transformation + * (i.e. its last row is equal to (0, 0, 0, 1)) and store the result in dest. + *

+ * In order to store the result in the same vector, use {@link #transformAffine(Vector4f)}. + * + * @see #transformAffine(Vector4f) + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformAffine(Vector4fc v, Vector4f dest); + + /** + * Transform/multiply the 4D-vector (x, y, z, w) by assuming that this matrix represents an {@link #isAffine() affine} transformation + * (i.e. its last row is equal to (0, 0, 0, 1)) and store the result in dest. + * + * @param x + * the x coordinate of the direction to transform + * @param y + * the y coordinate of the direction to transform + * @param z + * the z coordinate of the direction to transform + * @param w + * the w coordinate of the direction to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformAffine(float x, float y, float z, float w, Vector4f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix4f scale(Vector3fc xyz, Matrix4f dest); + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * Individual scaling of all three axes can be applied using {@link #scale(float, float, float, Matrix4f)}. + * + * @see #scale(float, float, float, Matrix4f) + * + * @param xyz + * the factor for all components + * @param dest + * will hold the result + * @return dest + */ + Matrix4f scale(float xyz, Matrix4f dest); + + /** + * Apply scaling to this matrix by by scaling the X axis by x and the Y axis by y + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix4f scaleXY(float x, float y, Matrix4f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix4f scale(float x, float y, float z, Matrix4f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).scale(sx, sy, sz).translate(-ox, -oy, -oz) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4f scaleAround(float sx, float sy, float sz, float ox, float oy, float oz, Matrix4f dest); + + /** + * Apply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).scale(factor).translate(-ox, -oy, -oz) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix4f scaleAround(float factor, float ox, float oy, float oz, Matrix4f dest); + + /** + * Pre-multiply scaling to this matrix by scaling all base axes by the given xyz factor, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param xyz + * the factor to scale all three base axes by + * @param dest + * will hold the result + * @return dest + */ + Matrix4f scaleLocal(float xyz, Matrix4f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix4f scaleLocal(float x, float y, float z, Matrix4f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using the given (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix4f().translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz).mul(this, dest) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4f scaleAroundLocal(float sx, float sy, float sz, float ox, float oy, float oz, Matrix4f dest); + + /** + * Pre-multiply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + *

+ * This method is equivalent to calling: new Matrix4f().translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz).mul(this, dest) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix4f scaleAroundLocal(float factor, float ox, float oy, float oz, Matrix4f dest); + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateX(float ang, Matrix4f dest); + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateY(float ang, Matrix4f dest); + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateZ(float ang, Matrix4f dest); + + /** + * Apply rotation about the Z axis to align the local +X towards (dirX, dirY) and store the result in dest. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * The vector (dirX, dirY) must be a unit vector. + * + * @param dirX + * the x component of the normalized direction + * @param dirY + * the y component of the normalized direction + * @param dest + * will hold the result + * @return this + */ + Matrix4f rotateTowardsXY(float dirX, float dirY, Matrix4f dest); + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX, dest).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateXYZ(float angleX, float angleY, float angleZ, Matrix4f dest); + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateAffineXYZ(float angleX, float angleY, float angleZ, Matrix4f dest); + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ, dest).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateZYX(float angleZ, float angleY, float angleX, Matrix4f dest); + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateAffineZYX(float angleZ, float angleY, float angleX, Matrix4f dest); + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY, dest).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateYXZ(float angleY, float angleX, float angleZ, Matrix4f dest); + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method assumes that this matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to (0, 0, 0, 1)) + * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination). + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateAffineYXZ(float angleY, float angleX, float angleZ, Matrix4f dest); + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotate(float ang, float x, float y, float z, Matrix4f dest); + + /** + * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateTranslation(float ang, float x, float y, float z, Matrix4f dest); + + /** + * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateAffine(float ang, float x, float y, float z, Matrix4f dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateLocal(float ang, float x, float y, float z, Matrix4f dest); + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateLocalX(float ang, Matrix4f dest); + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateLocalY(float ang, Matrix4f dest); + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateLocalZ(float ang, Matrix4f dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4f translate(Vector3fc offset, Matrix4f dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + Matrix4f translate(float x, float y, float z, Matrix4f dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4f translateLocal(Vector3fc offset, Matrix4f dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + Matrix4f translateLocal(float x, float y, float z, Matrix4f dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4f ortho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4f ortho(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4f dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4f dest); + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, boolean, Matrix4f) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4f orthoSymmetric(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, Matrix4f) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4f orthoSymmetric(float width, float height, float zNear, float zFar, Matrix4f dest); + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, boolean, Matrix4f) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4f orthoSymmetricLH(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, Matrix4f) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4f orthoSymmetricLH(float width, float height, float zNear, float zFar, Matrix4f dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix + * and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, Matrix4f) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float, Matrix4f) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + Matrix4f ortho2D(float left, float right, float bottom, float top, Matrix4f dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, Matrix4f) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float, Matrix4f) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + Matrix4f ortho2DLH(float left, float right, float bottom, float top, Matrix4f dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3fc, Vector3fc, Vector3fc, Matrix4f) lookAt} + * with eye = (0, 0, 0) and center = dir. + * + * @see #lookAlong(float, float, float, float, float, float, Matrix4f) + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc, Matrix4f) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lookAlong(Vector3fc dir, Vector3fc up, Matrix4f dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(float, float, float, float, float, float, float, float, float, Matrix4f) lookAt()} + * with eye = (0, 0, 0) and center = dir. + * + * @see #lookAt(float, float, float, float, float, float, float, float, float, Matrix4f) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4f dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAt(float, float, float, float, float, float, float, float, float, Matrix4f) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lookAt(Vector3fc eye, Vector3fc center, Vector3fc up, Matrix4f dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc, Matrix4f) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lookAt(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ, Matrix4f dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * This method assumes this to be a perspective transformation, obtained via + * {@link #frustum(float, float, float, float, float, float, Matrix4f) frustum()} or {@link #perspective(float, float, float, float, Matrix4f) perspective()} or + * one of their overloads. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lookAtPerspective(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ, Matrix4f dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAtLH(float, float, float, float, float, float, float, float, float, Matrix4f) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lookAtLH(Vector3fc eye, Vector3fc center, Vector3fc up, Matrix4f dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAtLH(Vector3fc, Vector3fc, Vector3fc, Matrix4f) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lookAtLH(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ, Matrix4f dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * This method assumes this to be a perspective transformation, obtained via + * {@link #frustumLH(float, float, float, float, float, float, Matrix4f) frustumLH()} or {@link #perspectiveLH(float, float, float, float, Matrix4f) perspectiveLH()} or + * one of their overloads. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lookAtPerspectiveLH(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ, Matrix4f dest); + + /** + * This method is equivalent to calling: translate(w-1-2*x, h-1-2*y, 0, dest).scale(w, h, 1) + *

+ * If M is this matrix and T the created transformation matrix, + * then the new matrix will be M * T. So when transforming a + * vector v with the new matrix by using M * T * v, the + * created transformation will be applied first! + * + * @param x + * the tile's x coordinate/index (should be in [0..w)) + * @param y + * the tile's y coordinate/index (should be in [0..h)) + * @param w + * the number of tiles along the x axis + * @param h + * the number of tiles along the y axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4f tile(int x, int y, int w, int h, Matrix4f dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4f perspective(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4f perspective(float fovy, float aspect, float zNear, float zFar, Matrix4f dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4f perspectiveRect(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4f perspectiveRect(float width, float height, float zNear, float zFar, Matrix4f dest); + + /** + * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + Matrix4f perspectiveRect(float width, float height, float zNear, float zFar, boolean zZeroToOne); + + /** + * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param width + * the width of the near frustum plane + * @param height + * the height of the near frustum plane + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + Matrix4f perspectiveRect(float width, float height, float zNear, float zFar); + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4f perspectiveOffCenter(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4f perspectiveOffCenter(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar, Matrix4f dest); + + /** + * Apply an asymmetric off-center perspective projection frustum transformation using for a right-handed coordinate system + * the given NDC z range to this matrix. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + Matrix4f perspectiveOffCenter(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar, boolean zZeroToOne); + + /** + * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * The given angles offAngleX and offAngleY are the horizontal and vertical angles between + * the line of sight and the line given by the center of the near and far frustum planes. So, when offAngleY + * is just fovy/2 then the projection frustum is rotated towards +Y and the bottom frustum plane + * is parallel to the XZ-plane. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param offAngleX + * the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param offAngleY + * the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @return this + */ + Matrix4f perspectiveOffCenter(float fovy, float offAngleX, float offAngleY, float aspect, float zNear, float zFar); + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4f perspectiveLH(float fovy, float aspect, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and P the perspective projection matrix, + * then the new matrix will be M * P. So when transforming a + * vector v with the new matrix by using M * P * v, + * the perspective projection will be applied first! + * + * @param fovy + * the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI}) + * @param aspect + * the aspect ratio (i.e. width / height; must be greater than zero) + * @param zNear + * near clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4f perspectiveLH(float fovy, float aspect, float zNear, float zFar, Matrix4f dest); + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4f frustum(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4f frustum(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4f dest); + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4f frustumLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4f dest); + + /** + * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and F the frustum matrix, + * then the new matrix will be M * F. So when transforming a + * vector v with the new matrix by using M * F * v, + * the frustum transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance along the x-axis to the left frustum edge + * @param right + * the distance along the x-axis to the right frustum edge + * @param bottom + * the distance along the y-axis to the bottom frustum edge + * @param top + * the distance along the y-axis to the top frustum edge + * @param zNear + * near clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity. + * In that case, zFar may not also be {@link Float#POSITIVE_INFINITY}. + * @param zFar + * far clipping plane distance. This value must be greater than zero. + * If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity. + * In that case, zNear may not also be {@link Float#POSITIVE_INFINITY}. + * @param dest + * will hold the result + * @return dest + */ + Matrix4f frustumLH(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4f dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotate(Quaternionfc quat, Matrix4f dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this {@link #isAffine() affine} matrix and store + * the result in dest. + *

+ * This method assumes this to be {@link #isAffine() affine}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateAffine(Quaternionfc quat, Matrix4f dest); + + /** + * Apply the rotation - and possibly scaling - ransformation of the given {@link Quaternionfc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateTranslation(Quaternionfc quat, Matrix4f dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this {@link #isAffine() affine} + * matrix while using (ox, oy, oz) as the rotation origin, and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is only applicable if this is an {@link #isAffine() affine} matrix. + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateAroundAffine(Quaternionfc quat, float ox, float oy, float oz, Matrix4f dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix while using (ox, oy, oz) as the rotation origin, + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateAround(Quaternionfc quat, float ox, float oy, float oz, Matrix4f dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateLocal(Quaternionfc quat, Matrix4f dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix while using (ox, oy, oz) + * as the rotation origin, and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * This method is equivalent to calling: translateLocal(-ox, -oy, -oz, dest).rotateLocal(quat).translateLocal(ox, oy, oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateAroundLocal(Quaternionfc quat, float ox, float oy, float oz, Matrix4f dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float, Matrix4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotate(AxisAngle4f axisAngle, Matrix4f dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis-angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float, Matrix4f) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3fc#normalize(Vector3f) normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotate(float angle, Vector3fc axis, Matrix4f dest); + + /** + * Unproject the given window coordinates (winX, winY, winZ) by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * The depth range of winZ is assumed to be [0..1], which is also the OpenGL default. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4f)} and then the method {@link #unprojectInv(float, float, float, int[], Vector4f) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(float, float, float, int[], Vector4f) + * @see #invert(Matrix4f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param winZ + * the z-coordinate, which is the depth value in [0..1] + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector4f unproject(float winX, float winY, float winZ, int[] viewport, Vector4f dest); + + /** + * Unproject the given window coordinates (winX, winY, winZ) by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * The depth range of winZ is assumed to be [0..1], which is also the OpenGL default. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4f)} and then the method {@link #unprojectInv(float, float, float, int[], Vector3f) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(float, float, float, int[], Vector3f) + * @see #invert(Matrix4f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param winZ + * the z-coordinate, which is the depth value in [0..1] + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector3f unproject(float winX, float winY, float winZ, int[] viewport, Vector3f dest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * The depth range of winCoords.z is assumed to be [0..1], which is also the OpenGL default. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4f)} and then the method {@link #unprojectInv(float, float, float, int[], Vector4f) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(float, float, float, int[], Vector4f) + * @see #unproject(float, float, float, int[], Vector4f) + * @see #invert(Matrix4f) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector4f unproject(Vector3fc winCoords, int[] viewport, Vector4f dest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * The depth range of winCoords.z is assumed to be [0..1], which is also the OpenGL default. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4f)} and then the method {@link #unprojectInv(float, float, float, int[], Vector3f) unprojectInv()} can be invoked on it. + * + * @see #unprojectInv(float, float, float, int[], Vector3f) + * @see #unproject(float, float, float, int[], Vector3f) + * @see #invert(Matrix4f) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector3f unproject(Vector3fc winCoords, int[] viewport, Vector3f dest); + + /** + * Unproject the given 2D window coordinates (winX, winY) by this matrix using the specified viewport + * and compute the origin and the direction of the resulting ray which starts at NDC z = -1.0 and goes through NDC z = +1.0. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4f)} and then the method {@link #unprojectInvRay(float, float, int[], Vector3f, Vector3f) unprojectInvRay()} can be invoked on it. + * + * @see #unprojectInvRay(float, float, int[], Vector3f, Vector3f) + * @see #invert(Matrix4f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param originDest + * will hold the ray origin + * @param dirDest + * will hold the (unnormalized) ray direction + * @return this + */ + Matrix4f unprojectRay(float winX, float winY, int[] viewport, Vector3f originDest, Vector3f dirDest); + + /** + * Unproject the given 2D window coordinates winCoords by this matrix using the specified viewport + * and compute the origin and the direction of the resulting ray which starts at NDC z = -1.0 and goes through NDC z = +1.0. + *

+ * This method first converts the given window coordinates to normalized device coordinates in the range [-1..1] + * and then transforms those NDC coordinates by the inverse of this matrix. + *

+ * As a necessary computation step for unprojecting, this method computes the inverse of this matrix. + * In order to avoid computing the matrix inverse with every invocation, the inverse of this matrix can be built + * once outside using {@link #invert(Matrix4f)} and then the method {@link #unprojectInvRay(float, float, int[], Vector3f, Vector3f) unprojectInvRay()} can be invoked on it. + * + * @see #unprojectInvRay(float, float, int[], Vector3f, Vector3f) + * @see #unprojectRay(float, float, int[], Vector3f, Vector3f) + * @see #invert(Matrix4f) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param originDest + * will hold the ray origin + * @param dirDest + * will hold the (unnormalized) ray direction + * @return this + */ + Matrix4f unprojectRay(Vector2fc winCoords, int[] viewport, Vector3f originDest, Vector3f dirDest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(Vector3fc, int[], Vector4f) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + *

+ * The depth range of winCoords.z is assumed to be [0..1], which is also the OpenGL default. + *

+ * This method reads the four viewport parameters from the given int[]. + * + * @see #unproject(Vector3fc, int[], Vector4f) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector4f unprojectInv(Vector3fc winCoords, int[] viewport, Vector4f dest); + + /** + * Unproject the given window coordinates (winX, winY, winZ) by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(float, float, float, int[], Vector4f) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + *

+ * The depth range of winZ is assumed to be [0..1], which is also the OpenGL default. + * + * @see #unproject(float, float, float, int[], Vector4f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param winZ + * the z-coordinate, which is the depth value in [0..1] + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector4f unprojectInv(float winX, float winY, float winZ, int[] viewport, Vector4f dest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport + * and compute the origin and the direction of the resulting ray which starts at NDC z = -1.0 and goes through NDC z = +1.0. + *

+ * This method differs from {@link #unprojectRay(Vector2fc, int[], Vector3f, Vector3f) unprojectRay()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + * + * @see #unprojectRay(Vector2fc, int[], Vector3f, Vector3f) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param originDest + * will hold the ray origin + * @param dirDest + * will hold the (unnormalized) ray direction + * @return this + */ + Matrix4f unprojectInvRay(Vector2fc winCoords, int[] viewport, Vector3f originDest, Vector3f dirDest); + + /** + * Unproject the given 2D window coordinates (winX, winY) by this matrix using the specified viewport + * and compute the origin and the direction of the resulting ray which starts at NDC z = -1.0 and goes through NDC z = +1.0. + *

+ * This method differs from {@link #unprojectRay(float, float, int[], Vector3f, Vector3f) unprojectRay()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + * + * @see #unprojectRay(float, float, int[], Vector3f, Vector3f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param viewport + * the viewport described by [x, y, width, height] + * @param originDest + * will hold the ray origin + * @param dirDest + * will hold the (unnormalized) ray direction + * @return this + */ + Matrix4f unprojectInvRay(float winX, float winY, int[] viewport, Vector3f originDest, Vector3f dirDest); + + /** + * Unproject the given window coordinates winCoords by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(Vector3fc, int[], Vector3f) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + *

+ * The depth range of winCoords.z is assumed to be [0..1], which is also the OpenGL default. + * + * @see #unproject(Vector3fc, int[], Vector3f) + * + * @param winCoords + * the window coordinates to unproject + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector3f unprojectInv(Vector3fc winCoords, int[] viewport, Vector3f dest); + + /** + * Unproject the given window coordinates (winX, winY, winZ) by this matrix using the specified viewport. + *

+ * This method differs from {@link #unproject(float, float, float, int[], Vector3f) unproject()} + * in that it assumes that this is already the inverse matrix of the original projection matrix. + * It exists to avoid recomputing the matrix inverse with every invocation. + *

+ * The depth range of winZ is assumed to be [0..1], which is also the OpenGL default. + * + * @see #unproject(float, float, float, int[], Vector3f) + * + * @param winX + * the x-coordinate in window coordinates (pixels) + * @param winY + * the y-coordinate in window coordinates (pixels) + * @param winZ + * the z-coordinate, which is the depth value in [0..1] + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * will hold the unprojected position + * @return dest + */ + Vector3f unprojectInv(float winX, float winY, float winZ, int[] viewport, Vector3f dest); + + /** + * Project the given (x, y, z) position via this matrix using the specified viewport + * and store the resulting window coordinates in winCoordsDest. + *

+ * This method transforms the given coordinates by this matrix including perspective division to + * obtain normalized device coordinates, and then translates these into window coordinates by using the + * given viewport settings [x, y, width, height]. + *

+ * The depth range of the returned winCoordsDest.z will be [0..1], which is also the OpenGL default. + * + * @param x + * the x-coordinate of the position to project + * @param y + * the y-coordinate of the position to project + * @param z + * the z-coordinate of the position to project + * @param viewport + * the viewport described by [x, y, width, height] + * @param winCoordsDest + * will hold the projected window coordinates + * @return winCoordsDest + */ + Vector4f project(float x, float y, float z, int[] viewport, Vector4f winCoordsDest); + + /** + * Project the given (x, y, z) position via this matrix using the specified viewport + * and store the resulting window coordinates in winCoordsDest. + *

+ * This method transforms the given coordinates by this matrix including perspective division to + * obtain normalized device coordinates, and then translates these into window coordinates by using the + * given viewport settings [x, y, width, height]. + *

+ * The depth range of the returned winCoordsDest.z will be [0..1], which is also the OpenGL default. + * + * @param x + * the x-coordinate of the position to project + * @param y + * the y-coordinate of the position to project + * @param z + * the z-coordinate of the position to project + * @param viewport + * the viewport described by [x, y, width, height] + * @param winCoordsDest + * will hold the projected window coordinates + * @return winCoordsDest + */ + Vector3f project(float x, float y, float z, int[] viewport, Vector3f winCoordsDest); + + /** + * Project the given position via this matrix using the specified viewport + * and store the resulting window coordinates in winCoordsDest. + *

+ * This method transforms the given coordinates by this matrix including perspective division to + * obtain normalized device coordinates, and then translates these into window coordinates by using the + * given viewport settings [x, y, width, height]. + *

+ * The depth range of the returned winCoordsDest.z will be [0..1], which is also the OpenGL default. + * + * @see #project(float, float, float, int[], Vector4f) + * + * @param position + * the position to project into window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @param winCoordsDest + * will hold the projected window coordinates + * @return winCoordsDest + */ + Vector4f project(Vector3fc position, int[] viewport, Vector4f winCoordsDest); + + /** + * Project the given position via this matrix using the specified viewport + * and store the resulting window coordinates in winCoordsDest. + *

+ * This method transforms the given coordinates by this matrix including perspective division to + * obtain normalized device coordinates, and then translates these into window coordinates by using the + * given viewport settings [x, y, width, height]. + *

+ * The depth range of the returned winCoordsDest.z will be [0..1], which is also the OpenGL default. + * + * @see #project(float, float, float, int[], Vector4f) + * + * @param position + * the position to project into window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @param winCoordsDest + * will hold the projected window coordinates + * @return winCoordsDest + */ + Vector3f project(Vector3fc position, int[] viewport, Vector3f winCoordsDest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0 and store the result in dest. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4f reflect(float a, float b, float c, float d, Matrix4f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4f reflect(float nx, float ny, float nz, float px, float py, float pz, Matrix4f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation and a point on the plane, and store the result in dest. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation relative to an implied normal vector of (0, 0, 1) + * @param point + * a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4f reflect(Quaternionfc orientation, Vector3fc point, Matrix4f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4f reflect(Vector3fc normal, Vector3fc point, Matrix4f dest); + + /** + * Get the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..3] + * @param dest + * will hold the row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..3] + */ + Vector4f getRow(int row, Vector4f dest) throws IndexOutOfBoundsException; + + /** + * Get the first three components of the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..3] + * @param dest + * will hold the first three row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..3] + */ + Vector3f getRow(int row, Vector3f dest) throws IndexOutOfBoundsException; + + /** + * Get the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param dest + * will hold the column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + Vector4f getColumn(int column, Vector4f dest) throws IndexOutOfBoundsException; + + /** + * Get the first three components of the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param dest + * will hold the first three column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + Vector3f getColumn(int column, Vector3f dest) throws IndexOutOfBoundsException; + + /** + * Get the matrix element value at the given column and row. + * + * @param column + * the colum index in [0..3] + * @param row + * the row index in [0..3] + * @return the element value + */ + float get(int column, int row); + + /** + * Get the matrix element value at the given row and column. + * + * @param row + * the row index in [0..3] + * @param column + * the colum index in [0..3] + * @return the element value + */ + float getRowColumn(int row, int column); + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into the upper left 3x3 submatrix of dest. + * All other values of dest will be set to identity. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f normal(Matrix4f dest); + + /** + * Compute a normal matrix from the upper left 3x3 submatrix of this + * and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @see Matrix3f#set(Matrix4fc) + * @see #get3x3(Matrix3f) + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f normal(Matrix3f dest); + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this + * and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f cofactor3x3(Matrix3f dest); + + /** + * Compute the cofactor matrix of the upper left 3x3 submatrix of this + * and store it into dest. + * All other values of dest will be set to identity. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix4f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f cofactor3x3(Matrix4f dest); + + /** + * Normalize the upper left 3x3 submatrix of this matrix and store the result in dest. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f normalize3x3(Matrix4f dest); + + /** + * Normalize the upper left 3x3 submatrix of this matrix and store the result in dest. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f normalize3x3(Matrix3f dest); + + /** + * Calculate a frustum plane of this matrix, which + * can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given planeEquation. + *

+ * Generally, this method computes the frustum plane in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * The frustum plane will be given in the form of a general plane equation: + * a*x + b*y + c*z + d = 0, where the given {@link Vector4f} components will + * hold the (a, b, c, d) values of the equation. + *

+ * The plane normal, which is (a, b, c), is directed "inwards" of the frustum. + * Any plane/point test using a*x + b*y + c*z + d therefore will yield a result greater than zero + * if the point is within the frustum (i.e. at the positive side of the frustum plane). + *

+ * For performing frustum culling, the class {@link FrustumIntersection} should be used instead of + * manually obtaining the frustum planes and testing them against points, spheres or axis-aligned boxes. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param plane + * one of the six possible planes, given as numeric constants + * {@link #PLANE_NX}, {@link #PLANE_PX}, + * {@link #PLANE_NY}, {@link #PLANE_PY}, + * {@link #PLANE_NZ} and {@link #PLANE_PZ} + * @param planeEquation + * will hold the computed plane equation. + * The plane equation will be normalized, meaning that (a, b, c) will be a unit vector + * @return planeEquation + */ + Vector4f frustumPlane(int plane, Vector4f planeEquation); + + /** + * Compute the corner coordinates of the frustum defined by this matrix, which + * can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given point. + *

+ * Generally, this method computes the frustum corners in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * Reference: http://geomalgorithms.com + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param corner + * one of the eight possible corners, given as numeric constants + * {@link #CORNER_NXNYNZ}, {@link #CORNER_PXNYNZ}, {@link #CORNER_PXPYNZ}, {@link #CORNER_NXPYNZ}, + * {@link #CORNER_PXNYPZ}, {@link #CORNER_NXNYPZ}, {@link #CORNER_NXPYPZ}, {@link #CORNER_PXPYPZ} + * @param point + * will hold the resulting corner point coordinates + * @return point + */ + Vector3f frustumCorner(int corner, Vector3f point); + + /** + * Compute the eye/origin of the perspective frustum transformation defined by this matrix, + * which can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given origin. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(float, float, float, float, Matrix4f) perspective()} + * or {@link #frustum(float, float, float, float, float, float, Matrix4f) frustum()}. + *

+ * Generally, this method computes the origin in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * This method is equivalent to calling: invert(new Matrix4f()).transformProject(0, 0, -1, 0, origin) + * and in the case of an already available inverse of this matrix, the method {@link #perspectiveInvOrigin(Vector3f)} + * on the inverse of the matrix should be used instead. + *

+ * Reference: http://geomalgorithms.com + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param origin + * will hold the origin of the coordinate system before applying this + * perspective projection transformation + * @return origin + */ + Vector3f perspectiveOrigin(Vector3f origin); + + /** + * Compute the eye/origin of the inverse of the perspective frustum transformation defined by this matrix, + * which can be the inverse of a projection matrix or the inverse of a combined modelview-projection matrix, and store the result + * in the given dest. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(float, float, float, float, Matrix4f) perspective()} + * or {@link #frustum(float, float, float, float, float, float, Matrix4f) frustum()}. + *

+ * If the inverse of the modelview-projection matrix is not available, then calling {@link #perspectiveOrigin(Vector3f)} + * on the original modelview-projection matrix is preferred. + * + * @see #perspectiveOrigin(Vector3f) + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f perspectiveInvOrigin(Vector3f dest); + + /** + * Return the vertical field-of-view angle in radians of this perspective transformation matrix. + *

+ * Note that this method will only work using perspective projections obtained via one of the + * perspective methods, such as {@link #perspective(float, float, float, float, Matrix4f) perspective()} + * or {@link #frustum(float, float, float, float, float, float, Matrix4f) frustum()}. + *

+ * For orthogonal transformations this method will return 0.0. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @return the vertical field-of-view angle in radians + */ + float perspectiveFov(); + + /** + * Extract the near clip plane distance from this perspective projection matrix. + *

+ * This method only works if this is a perspective projection matrix, for example obtained via {@link #perspective(float, float, float, float, Matrix4f)}. + * + * @return the near clip plane distance + */ + float perspectiveNear(); + + /** + * Extract the far clip plane distance from this perspective projection matrix. + *

+ * This method only works if this is a perspective projection matrix, for example obtained via {@link #perspective(float, float, float, float, Matrix4f)}. + * + * @return the far clip plane distance + */ + float perspectiveFar(); + + /** + * Obtain the direction of a ray starting at the center of the coordinate system and going + * through the near frustum plane. + *

+ * This method computes the dir vector in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * The parameters x and y are used to interpolate the generated ray direction + * from the bottom-left to the top-right frustum corners. + *

+ * For optimal efficiency when building many ray directions over the whole frustum, + * it is recommended to use this method only in order to compute the four corner rays at + * (0, 0), (1, 0), (0, 1) and (1, 1) + * and then bilinearly interpolating between them; or to use the {@link FrustumRayBuilder}. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the interpolation factor along the left-to-right frustum planes, within [0..1] + * @param y + * the interpolation factor along the bottom-to-top frustum planes, within [0..1] + * @param dir + * will hold the normalized ray direction in the local frame of the coordinate system before + * transforming to homogeneous clipping space using this matrix + * @return dir + */ + Vector3f frustumRayDir(float x, float y, Vector3f dir); + + /** + * Obtain the direction of +Z before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +Z by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).invert();
+     * inv.transformDirection(dir.set(0, 0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveZ(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3f positiveZ(Vector3f dir); + + /** + * Obtain the direction of +Z before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +Z by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).transpose();
+     * inv.transformDirection(dir.set(0, 0, 1));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3f normalizedPositiveZ(Vector3f dir); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).invert();
+     * inv.transformDirection(dir.set(1, 0, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3f positiveX(Vector3f dir); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).transpose();
+     * inv.transformDirection(dir.set(1, 0, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3f normalizedPositiveX(Vector3f dir); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).invert();
+     * inv.transformDirection(dir.set(0, 1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3f positiveY(Vector3f dir); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the upper left 3x3 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).transpose();
+     * inv.transformDirection(dir.set(0, 1, 0));
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3f normalizedPositiveY(Vector3f dir); + + /** + * Obtain the position that gets transformed to the origin by this {@link #isAffine() affine} matrix. + * This can be used to get the position of the "camera" from a given view transformation matrix. + *

+ * This method only works with {@link #isAffine() affine} matrices. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).invertAffine();
+     * inv.transformPosition(origin.set(0, 0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + Vector3f originAffine(Vector3f origin); + + /** + * Obtain the position that gets transformed to the origin by this matrix. + * This can be used to get the position of the "camera" from a given view/projection transformation matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4f inv = new Matrix4f(this).invert();
+     * inv.transformPosition(origin.set(0, 0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + Vector3f origin(Vector3f origin); + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction light + * and store the result in dest. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param light + * the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4f shadow(Vector4f light, float a, float b, float c, float d, Matrix4f dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW) + * and store the result in dest. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param lightX + * the x-component of the light's vector + * @param lightY + * the y-component of the light's vector + * @param lightZ + * the z-component of the light's vector + * @param lightW + * the w-component of the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4f shadow(float lightX, float lightY, float lightZ, float lightW, float a, float b, float c, float d, Matrix4f dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction light + * and store the result in dest. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param light + * the light's vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @param dest + * will hold the result + * @return dest + */ + Matrix4f shadow(Vector4f light, Matrix4fc planeTransform, Matrix4f dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW) + * and store the result in dest. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param lightX + * the x-component of the light vector + * @param lightY + * the y-component of the light vector + * @param lightZ + * the z-component of the light vector + * @param lightW + * the w-component of the light vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @param dest + * will hold the result + * @return dest + */ + Matrix4f shadow(float lightX, float lightY, float lightZ, float lightW, Matrix4fc planeTransform, Matrix4f dest); + + /** + * Apply a picking transformation to this matrix using the given window coordinates (x, y) as the pick center + * and the given (width, height) as the size of the picking region in window coordinates, and store the result + * in dest. + * + * @param x + * the x coordinate of the picking region center in window coordinates + * @param y + * the y coordinate of the picking region center in window coordinates + * @param width + * the width of the picking region in window coordinates + * @param height + * the height of the picking region in window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4f pick(float x, float y, float width, float height, int[] viewport, Matrix4f dest); + + /** + * Determine whether this matrix describes an affine transformation. This is the case iff its last row is equal to (0, 0, 0, 1). + * + * @return true iff this matrix is affine; false otherwise + */ + boolean isAffine(); + + /** + * Apply an arcball view transformation to this matrix with the given radius and center (centerX, centerY, centerZ) + * position of the arcball and the specified X and Y rotation angles, and store the result in dest. + *

+ * This method is equivalent to calling: translate(0, 0, -radius, dest).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ) + * + * @param radius + * the arcball radius + * @param centerX + * the x coordinate of the center position of the arcball + * @param centerY + * the y coordinate of the center position of the arcball + * @param centerZ + * the z coordinate of the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4f arcball(float radius, float centerX, float centerY, float centerZ, float angleX, float angleY, Matrix4f dest); + + /** + * Apply an arcball view transformation to this matrix with the given radius and center + * position of the arcball and the specified X and Y rotation angles, and store the result in dest. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z) + * + * @param radius + * the arcball radius + * @param center + * the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4f arcball(float radius, Vector3fc center, float angleX, float angleY, Matrix4f dest); + + /** + * Compute the axis-aligned bounding box of the frustum described by this matrix and store the minimum corner + * coordinates in the given min and the maximum corner coordinates in the given max vector. + *

+ * The matrix this is assumed to be the {@link #invert(Matrix4f) inverse} of the origial view-projection matrix + * for which to compute the axis-aligned bounding box in world-space. + *

+ * The axis-aligned bounding box of the unit frustum is (-1, -1, -1), (1, 1, 1). + * + * @param min + * will hold the minimum corner coordinates of the axis-aligned bounding box + * @param max + * will hold the maximum corner coordinates of the axis-aligned bounding box + * @return this + */ + Matrix4f frustumAabb(Vector3f min, Vector3f max); + + /** + * Compute the range matrix for the Projected Grid transformation as described in chapter "2.4.2 Creating the range conversion matrix" + * of the paper Real-time water rendering - Introducing the projected grid concept + * based on the inverse of the view-projection matrix which is assumed to be this, and store that range matrix into dest. + *

+ * If the projected grid will not be visible then this method returns null. + *

+ * This method uses the y = 0 plane for the projection. + * + * @param projector + * the projector view-projection transformation + * @param sLower + * the lower (smallest) Y-coordinate which any transformed vertex might have while still being visible on the projected grid + * @param sUpper + * the upper (highest) Y-coordinate which any transformed vertex might have while still being visible on the projected grid + * @param dest + * will hold the resulting range matrix + * @return the computed range matrix; or null if the projected grid will not be visible + */ + Matrix4f projectedGridRange(Matrix4fc projector, float sLower, float sUpper, Matrix4f dest); + + /** + * Change the near and far clip plane distances of this perspective frustum transformation matrix + * and store the result in dest. + *

+ * This method only works if this is a perspective projection frustum transformation, for example obtained + * via {@link #perspective(float, float, float, float, Matrix4f) perspective()} or {@link #frustum(float, float, float, float, float, float, Matrix4f) frustum()}. + * + * @see #perspective(float, float, float, float, Matrix4f) + * @see #frustum(float, float, float, float, float, float, Matrix4f) + * + * @param near + * the new near clip plane distance + * @param far + * the new far clip plane distance + * @param dest + * will hold the resulting matrix + * @return dest + */ + Matrix4f perspectiveFrustumSlice(float near, float far, Matrix4f dest); + + /** + * Build an ortographic projection transformation that fits the view-projection transformation represented by this + * into the given affine view transformation. + *

+ * The transformation represented by this must be given as the {@link #invert(Matrix4f) inverse} of a typical combined camera view-projection + * transformation, whose projection can be either orthographic or perspective. + *

+ * The view must be an {@link #isAffine() affine} transformation which in the application of Cascaded Shadow Maps is usually the light view transformation. + * It be obtained via any affine transformation or for example via {@link #lookAt(float, float, float, float, float, float, float, float, float, Matrix4f) lookAt()}. + *

+ * Reference: OpenGL SDK - Cascaded Shadow Maps + * + * @param view + * the view transformation to build a corresponding orthographic projection to fit the frustum of this + * @param dest + * will hold the crop projection transformation + * @return dest + */ + Matrix4f orthoCrop(Matrix4fc view, Matrix4f dest); + + /** + * Transform the axis-aligned box given as the minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ) + * by this {@link #isAffine() affine} matrix and compute the axis-aligned box of the result whose minimum corner is stored in outMin + * and maximum corner stored in outMax. + *

+ * Reference: http://dev.theomader.com + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the y coordinate of the maximum corner of the axis-aligned box + * @param outMin + * will hold the minimum corner of the resulting axis-aligned box + * @param outMax + * will hold the maximum corner of the resulting axis-aligned box + * @return this + */ + Matrix4f transformAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, Vector3f outMin, Vector3f outMax); + + /** + * Transform the axis-aligned box given as the minimum corner min and maximum corner max + * by this {@link #isAffine() affine} matrix and compute the axis-aligned box of the result whose minimum corner is stored in outMin + * and maximum corner stored in outMax. + * + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param outMin + * will hold the minimum corner of the resulting axis-aligned box + * @param outMax + * will hold the maximum corner of the resulting axis-aligned box + * @return this + */ + Matrix4f transformAab(Vector3fc min, Vector3fc max, Vector3f outMin, Vector3f outMax); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Matrix4f lerp(Matrix4fc other, float t, Matrix4f dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mulAffine(new Matrix4f().lookAt(new Vector3f(), new Vector3f(dir).negate(), up).invertAffine(), dest) + * + * @see #rotateTowards(float, float, float, float, float, float, Matrix4f) + * + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateTowards(Vector3fc dir, Vector3fc up, Matrix4f dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ) + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mulAffine(new Matrix4f().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine(), dest) + * + * @see #rotateTowards(Vector3fc, Vector3fc, Matrix4f) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4f dest); + + /** + * Extract the Euler angles from the rotation represented by the upper left 3x3 submatrix of this + * and store the extracted Euler angles in dest. + *

+ * This method assumes that the upper left of this only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + *

+ * Note that the returned Euler angles must be applied in the order X * Y * Z to obtain the identical matrix. + * This means that calling {@link Matrix4fc#rotateXYZ(float, float, float, Matrix4f)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix4f m = ...; // <- matrix only representing rotation
+     * Matrix4f n = new Matrix4f();
+     * n.rotateXYZ(m.getEulerAnglesXYZ(new Vector3f()));
+     * 
+ *

+ * Reference: http://en.wikipedia.org/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3f getEulerAnglesXYZ(Vector3f dest); + + /** + * Extract the Euler angles from the rotation represented by the upper left 3x3 submatrix of this + * and store the extracted Euler angles in dest. + *

+ * This method assumes that the upper left of this only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + *

+ * Note that the returned Euler angles must be applied in the order Z * Y * X to obtain the identical matrix. + * This means that calling {@link Matrix4fc#rotateZYX(float, float, float, Matrix4f)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix4f m = ...; // <- matrix only representing rotation
+     * Matrix4f n = new Matrix4f();
+     * n.rotateZYX(m.getEulerAnglesZYX(new Vector3f()));
+     * 
+ *

+ * Reference: http://nghiaho.com/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3f getEulerAnglesZYX(Vector3f dest); + + /** + * Test whether the given point (x, y, z) is within the frustum defined by this matrix. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given point with the coordinates (x, y, z) given + * in space M is within the clip space. + *

+ * When testing multiple points using the same transformation matrix, {@link FrustumIntersection} should be used instead. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the x-coordinate of the point + * @param y + * the y-coordinate of the point + * @param z + * the z-coordinate of the point + * @return true if the given point is inside the frustum; false otherwise + */ + boolean testPoint(float x, float y, float z); + + /** + * Test whether the given sphere is partly or completely within or outside of the frustum defined by this matrix. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given sphere with the coordinates (x, y, z) given + * in space M is within the clip space. + *

+ * When testing multiple spheres using the same transformation matrix, or more sophisticated/optimized intersection algorithms are required, + * {@link FrustumIntersection} should be used instead. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns true for spheres that are actually not visible. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param x + * the x-coordinate of the sphere's center + * @param y + * the y-coordinate of the sphere's center + * @param z + * the z-coordinate of the sphere's center + * @param r + * the sphere's radius + * @return true if the given sphere is partly or completely inside the frustum; false otherwise + */ + boolean testSphere(float x, float y, float z, float r); + + /** + * Test whether the given axis-aligned box is partly or completely within or outside of the frustum defined by this matrix. + * The box is specified via its min and max corner coordinates. + *

+ * This method assumes this matrix to be a transformation from any arbitrary coordinate system/space M + * into standard OpenGL clip space and tests whether the given axis-aligned box with its minimum corner coordinates (minX, minY, minZ) + * and maximum corner coordinates (maxX, maxY, maxZ) given in space M is within the clip space. + *

+ * When testing multiple axis-aligned boxes using the same transformation matrix, or more sophisticated/optimized intersection algorithms are required, + * {@link FrustumIntersection} should be used instead. + *

+ * The algorithm implemented by this method is conservative. This means that in certain circumstances a false positive + * can occur, when the method returns -1 for boxes that are actually not visible/do not intersect the frustum. + * See iquilezles.org for an examination of this problem. + *

+ * Reference: Efficient View Frustum Culling + *
+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param minX + * the x-coordinate of the minimum corner + * @param minY + * the y-coordinate of the minimum corner + * @param minZ + * the z-coordinate of the minimum corner + * @param maxX + * the x-coordinate of the maximum corner + * @param maxY + * the y-coordinate of the maximum corner + * @param maxZ + * the z-coordinate of the maximum corner + * @return true if the axis-aligned box is completely or partly inside of the frustum; false otherwise + */ + boolean testAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ); + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + Matrix4f obliqueZ(float a, float b, Matrix4f dest); + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3f)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3f)}) and the + * given vector up, and store the result in dest. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from calling + * {@link Matrix4f#setLookAt(Vector3fc, Vector3fc, Vector3fc)} with the current + * local origin of this matrix (as obtained by {@link #originAffine(Vector3f)}), the sum of this position and the + * negated local Z axis as well as the given vector up. + *

+ * This method must only be called on {@link #isAffine()} matrices. + * + * @param up + * the up vector + * @param dest + * will hold the result + * @return this + */ + Matrix4f withLookAtUp(Vector3fc up, Matrix4f dest); + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3f)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3f)}) and the + * given vector (upX, upY, upZ), and store the result in dest. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from calling + * {@link Matrix4f#setLookAt(float, float, float, float, float, float, float, float, float)} called with the current + * local origin of this matrix (as obtained by {@link #originAffine(Vector3f)}), the sum of this position and the + * negated local Z axis as well as the given vector (upX, upY, upZ). + *

+ * This method must only be called on {@link #isAffine()} matrices. + * + * @param upX + * the x coordinate of the up vector + * @param upY + * the y coordinate of the up vector + * @param upZ + * the z coordinate of the up vector + * @param dest + * will hold the result + * @return this + */ + Matrix4f withLookAtUp(float upX, float upY, float upZ, Matrix4f dest); + + /** + * Multiply this by the matrix + *

+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 1 0 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapXZY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapXZnY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapXnYnZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapXnZY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapXnZnY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapYXZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapYXnZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 1 0 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapYZX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapYZnX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapYnXZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapYnXnZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapYnZX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapYnZnX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapZXY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapZXnY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapZYX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapZYnX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapZnXY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapZnXnY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapZnYX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapZnYnX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0 -1 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnXYnZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 1 0 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnXZY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 1  0 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnXZnY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 1 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnXnYZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0 -1 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnXnYnZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0 -1 0 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnXnZY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0 -1  0 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnXnZnY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnYXZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnYXnZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnYZX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnYZnX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnYnXZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnYnXnZ(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnYnZX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnYnZnX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnZXY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnZXnY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnZYX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnZYnX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnZnXY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnZnXnY(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnZnYX(Matrix4f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f mapnZnYnX(Matrix4f dest); + + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 1 0
+     *  0 0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f negateX(Matrix4f dest); + + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 0  0 0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f negateY(Matrix4f dest); + + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 0 0  0 1
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f negateZ(Matrix4f dest); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix4fc m, float delta); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3d.java new file mode 100644 index 000000000..d28d634cc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3d.java @@ -0,0 +1,10698 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of an affine 4x3 matrix (4 columns, 3 rows) of doubles, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10 m20 m30
+ * m01 m11 m21 m31
+ * m02 m12 m22 m32
+ * + * @author Richard Greenlees + * @author Kai Burjack + */ +public class Matrix4x3d implements Externalizable, Cloneable, Matrix4x3dc { + + private static final long serialVersionUID = 1L; + + double m00, m01, m02; + double m10, m11, m12; + double m20, m21, m22; + double m30, m31, m32; + + int properties; + + /** + * Create a new {@link Matrix4x3d} and set it to {@link #identity() identity}. + */ + public Matrix4x3d() { + m00 = 1.0; + m11 = 1.0; + m22 = 1.0; + properties = PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + } + + /** + * Create a new {@link Matrix4x3d} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix4x3dc} to copy the values from + */ + public Matrix4x3d(Matrix4x3dc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4x3d} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix4x3fc} to copy the values from + */ + public Matrix4x3d(Matrix4x3fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4x3d} by setting its left 3x3 submatrix to the values of the given {@link Matrix3dc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix3dc} + */ + public Matrix4x3d(Matrix3dc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4x3d} by setting its left 3x3 submatrix to the values of the given {@link Matrix3fc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix3dc} + */ + public Matrix4x3d(Matrix3fc mat) { + set(mat); + } + + /** + * Create a new 4x4 matrix using the supplied double values. + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m02 + * the value of m02 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + * @param m12 + * the value of m12 + * @param m20 + * the value of m20 + * @param m21 + * the value of m21 + * @param m22 + * the value of m22 + * @param m30 + * the value of m30 + * @param m31 + * the value of m31 + * @param m32 + * the value of m32 + */ + public Matrix4x3d(double m00, double m01, double m02, + double m10, double m11, double m12, + double m20, double m21, double m22, + double m30, double m31, double m32) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + this.m30 = m30; + this.m31 = m31; + this.m32 = m32; + determineProperties(); + } + + /** + * Create a new {@link Matrix4x3d} by reading its 12 double components from the given {@link DoubleBuffer} + * at the buffer's current position. + *

+ * That DoubleBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link DoubleBuffer} to read the matrix values from + */ + public Matrix4x3d(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + determineProperties(); + } + + /** + * Assume the given properties about this matrix. + *

+ * Use one or multiple of 0, {@link Matrix4x3dc#PROPERTY_IDENTITY}, + * {@link Matrix4x3dc#PROPERTY_TRANSLATION}, {@link Matrix4x3dc#PROPERTY_ORTHONORMAL}. + * + * @param properties + * bitset of the properties to assume about this matrix + * @return this + */ + public Matrix4x3d assume(int properties) { + this.properties = properties; + return this; + } + + /** + * Compute and set the matrix properties returned by {@link #properties()} based + * on the current matrix element values. + * + * @return this + */ + public Matrix4x3d determineProperties() { + int properties = 0; + if (m00 == 1.0 && m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m11 == 1.0 && m12 == 0.0 + && m20 == 0.0 && m21 == 0.0 && m22 == 1.0) { + properties |= PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + if (m30 == 0.0 && m31 == 0.0 && m32 == 0.0) + properties |= PROPERTY_IDENTITY; + } + /* + * We do not determine orthogonality, since it would require arbitrary epsilons + * and is rather expensive (6 dot products) in the worst case. + */ + this.properties = properties; + return this; + } + + public int properties() { + return properties; + } + + public double m00() { + return m00; + } + public double m01() { + return m01; + } + public double m02() { + return m02; + } + public double m10() { + return m10; + } + public double m11() { + return m11; + } + public double m12() { + return m12; + } + public double m20() { + return m20; + } + public double m21() { + return m21; + } + public double m22() { + return m22; + } + public double m30() { + return m30; + } + public double m31() { + return m31; + } + public double m32() { + return m32; + } + + Matrix4x3d _properties(int properties) { + this.properties = properties; + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0 without updating the properties of the matrix. + * + * @param m00 + * the new value + * @return this + */ + Matrix4x3d _m00(double m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1 without updating the properties of the matrix. + * + * @param m01 + * the new value + * @return this + */ + Matrix4x3d _m01(double m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2 without updating the properties of the matrix. + * + * @param m02 + * the new value + * @return this + */ + Matrix4x3d _m02(double m02) { + this.m02 = m02; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0 without updating the properties of the matrix. + * + * @param m10 + * the new value + * @return this + */ + Matrix4x3d _m10(double m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1 without updating the properties of the matrix. + * + * @param m11 + * the new value + * @return this + */ + Matrix4x3d _m11(double m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2 without updating the properties of the matrix. + * + * @param m12 + * the new value + * @return this + */ + Matrix4x3d _m12(double m12) { + this.m12 = m12; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0 without updating the properties of the matrix. + * + * @param m20 + * the new value + * @return this + */ + Matrix4x3d _m20(double m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1 without updating the properties of the matrix. + * + * @param m21 + * the new value + * @return this + */ + Matrix4x3d _m21(double m21) { + this.m21 = m21; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2 without updating the properties of the matrix. + * + * @param m22 + * the new value + * @return this + */ + Matrix4x3d _m22(double m22) { + this.m22 = m22; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 0 without updating the properties of the matrix. + * + * @param m30 + * the new value + * @return this + */ + Matrix4x3d _m30(double m30) { + this.m30 = m30; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 1 without updating the properties of the matrix. + * + * @param m31 + * the new value + * @return this + */ + Matrix4x3d _m31(double m31) { + this.m31 = m31; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 2 without updating the properties of the matrix. + * + * @param m32 + * the new value + * @return this + */ + Matrix4x3d _m32(double m32) { + this.m32 = m32; + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + public Matrix4x3d m00(double m00) { + this.m00 = m00; + properties &= ~PROPERTY_ORTHONORMAL; + if (m00 != 1.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + public Matrix4x3d m01(double m01) { + this.m01 = m01; + properties &= ~PROPERTY_ORTHONORMAL; + if (m01 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2. + * + * @param m02 + * the new value + * @return this + */ + public Matrix4x3d m02(double m02) { + this.m02 = m02; + properties &= ~PROPERTY_ORTHONORMAL; + if (m02 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + public Matrix4x3d m10(double m10) { + this.m10 = m10; + properties &= ~PROPERTY_ORTHONORMAL; + if (m10 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + public Matrix4x3d m11(double m11) { + this.m11 = m11; + properties &= ~PROPERTY_ORTHONORMAL; + if (m11 != 1.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2. + * + * @param m12 + * the new value + * @return this + */ + public Matrix4x3d m12(double m12) { + this.m12 = m12; + properties &= ~PROPERTY_ORTHONORMAL; + if (m12 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + public Matrix4x3d m20(double m20) { + this.m20 = m20; + properties &= ~PROPERTY_ORTHONORMAL; + if (m20 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + public Matrix4x3d m21(double m21) { + this.m21 = m21; + properties &= ~PROPERTY_ORTHONORMAL; + if (m21 != 0.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2. + * + * @param m22 + * the new value + * @return this + */ + public Matrix4x3d m22(double m22) { + this.m22 = m22; + properties &= ~PROPERTY_ORTHONORMAL; + if (m22 != 1.0) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 0. + * + * @param m30 + * the new value + * @return this + */ + public Matrix4x3d m30(double m30) { + this.m30 = m30; + if (m30 != 0.0) + properties &= ~PROPERTY_IDENTITY; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 1. + * + * @param m31 + * the new value + * @return this + */ + public Matrix4x3d m31(double m31) { + this.m31 = m31; + if (m31 != 0.0) + properties &= ~PROPERTY_IDENTITY; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 2. + * + * @param m32 + * the new value + * @return this + */ + public Matrix4x3d m32(double m32) { + this.m32 = m32; + if (m32 != 0.0) + properties &= ~PROPERTY_IDENTITY; + return this; + } + + /** + * Reset this matrix to the identity. + *

+ * Please note that if a call to {@link #identity()} is immediately followed by a call to: + * {@link #translate(double, double, double) translate}, + * {@link #rotate(double, double, double, double) rotate}, + * {@link #scale(double, double, double) scale}, + * {@link #ortho(double, double, double, double, double, double) ortho}, + * {@link #ortho2D(double, double, double, double) ortho2D}, + * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt}, + * {@link #lookAlong(double, double, double, double, double, double) lookAlong}, + * or any of their overloads, then the call to {@link #identity()} can be omitted and the subsequent call replaced with: + * {@link #translation(double, double, double) translation}, + * {@link #rotation(double, double, double, double) rotation}, + * {@link #scaling(double, double, double) scaling}, + * {@link #setOrtho(double, double, double, double, double, double) setOrtho}, + * {@link #setOrtho2D(double, double, double, double) setOrtho2D}, + * {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt}, + * {@link #setLookAlong(double, double, double, double, double, double) setLookAlong}, + * or any of their overloads. + * + * @return this + */ + public Matrix4x3d identity() { + if ((properties & PROPERTY_IDENTITY) != 0) + return this; + m00 = 1.0; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 1.0; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Store the values of the given matrix m into this matrix. + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4x3d set(Matrix4x3dc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + m30 = m.m30(); + m31 = m.m31(); + m32 = m.m32(); + properties = m.properties(); + return this; + } + + /** + * Store the values of the given matrix m into this matrix. + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4x3d set(Matrix4x3fc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + m30 = m.m30(); + m31 = m.m31(); + m32 = m.m32(); + properties = m.properties(); + return this; + } + + /** + * Store the values of the upper 4x3 submatrix of m into this matrix. + * + * @see Matrix4dc#get4x3(Matrix4x3d) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4x3d set(Matrix4dc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + m30 = m.m30(); + m31 = m.m31(); + m32 = m.m32(); + properties = m.properties() & (PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return this; + } + + public Matrix4d get(Matrix4d dest) { + return dest.set4x3(this); + } + + /** + * Set the left 3x3 submatrix of this {@link Matrix4x3d} to the given {@link Matrix3dc} + * and the rest to identity. + * + * @see #Matrix4x3d(Matrix3dc) + * + * @param mat + * the {@link Matrix3dc} + * @return this + */ + public Matrix4x3d set(Matrix3dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + return determineProperties(); + } + + /** + * Set the left 3x3 submatrix of this {@link Matrix4x3d} to the given {@link Matrix3fc} + * and the rest to identity. + * + * @see #Matrix4x3d(Matrix3fc) + * + * @param mat + * the {@link Matrix3fc} + * @return this + */ + public Matrix4x3d set(Matrix3fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + return determineProperties(); + } + + /** + * Set the four columns of this matrix to the supplied vectors, respectively. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @param col3 + * the fourth column + * @return this + */ + public Matrix4x3d set(Vector3dc col0, + Vector3dc col1, + Vector3dc col2, + Vector3dc col3) { + this.m00 = col0.x(); + this.m01 = col0.y(); + this.m02 = col0.z(); + this.m10 = col1.x(); + this.m11 = col1.y(); + this.m12 = col1.z(); + this.m20 = col2.x(); + this.m21 = col2.y(); + this.m22 = col2.z(); + this.m30 = col3.x(); + this.m31 = col3.y(); + this.m32 = col3.z(); + return determineProperties(); + } + + /** + * Set the left 3x3 submatrix of this {@link Matrix4x3d} to that of the given {@link Matrix4x3dc} + * and don't change the other elements. + * + * @param mat + * the {@link Matrix4x3dc} + * @return this + */ + public Matrix4x3d set3x3(Matrix4x3dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + properties &= mat.properties(); + return this; + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + * @return this + */ + public Matrix4x3d set(AxisAngle4f axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double invLength = Math.invsqrt(x*x + y*y + z*z); + x *= invLength; + y *= invLength; + z *= invLength; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0 - c; + m00 = c + x*x*omc; + m11 = c + y*y*omc; + m22 = c + z*z*omc; + double tmp1 = x*y*omc; + double tmp2 = z*s; + m10 = tmp1 - tmp2; + m01 = tmp1 + tmp2; + tmp1 = x*z*omc; + tmp2 = y*s; + m20 = tmp1 + tmp2; + m02 = tmp1 - tmp2; + tmp1 = y*z*omc; + tmp2 = x*s; + m21 = tmp1 - tmp2; + m12 = tmp1 + tmp2; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + * @return this + */ + public Matrix4x3d set(AxisAngle4d axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double invLength = Math.invsqrt(x*x + y*y + z*z); + x *= invLength; + y *= invLength; + z *= invLength; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0 - c; + m00 = c + x*x*omc; + m11 = c + y*y*omc; + m22 = c + z*z*omc; + double tmp1 = x*y*omc; + double tmp2 = z*s; + m10 = tmp1 - tmp2; + m01 = tmp1 + tmp2; + tmp1 = x*z*omc; + tmp2 = y*s; + m20 = tmp1 + tmp2; + m02 = tmp1 - tmp2; + tmp1 = y*z*omc; + tmp2 = x*s; + m21 = tmp1 - tmp2; + m12 = tmp1 + tmp2; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaternionfc}. + *

+ * This method is equivalent to calling: rotation(q) + * + * @see #rotation(Quaternionfc) + * + * @param q + * the {@link Quaternionfc} + * @return this + */ + public Matrix4x3d set(Quaternionfc q) { + return rotation(q); + } + + /** + * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaterniondc}. + *

+ * This method is equivalent to calling: rotation(q) + * + * @param q + * the {@link Quaterniondc} + * @return this + */ + public Matrix4x3d set(Quaterniondc q) { + return rotation(q); + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the multiplication + * @return this + */ + public Matrix4x3d mul(Matrix4x3dc right) { + return mul(right, this); + } + + public Matrix4x3d mul(Matrix4x3dc right, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return mulTranslation(right, dest); + return mulGeneric(right, dest); + } + private Matrix4x3d mulGeneric(Matrix4x3dc right, Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + double m20 = this.m20, m21 = this.m21, m22 = this.m22; + double rm00 = right.m00(), rm01 = right.m01(), rm02 = right.m02(); + double rm10 = right.m10(), rm11 = right.m11(), rm12 = right.m12(); + double rm20 = right.m20(), rm21 = right.m21(), rm22 = right.m22(); + double rm30 = right.m30(), rm31 = right.m31(), rm32 = right.m32(); + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30)))) + ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31)))) + ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32)))) + ._properties(properties & right.properties() & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this matrix by the supplied right matrix. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the multiplication + * @return this + */ + public Matrix4x3d mul(Matrix4x3fc right) { + return mul(right, this); + } + + public Matrix4x3d mul(Matrix4x3fc right, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return mulTranslation(right, dest); + return mulGeneric(right, dest); + } + private Matrix4x3d mulGeneric(Matrix4x3fc right, Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + double m20 = this.m20, m21 = this.m21, m22 = this.m22; + double rm00 = right.m00(), rm01 = right.m01(), rm02 = right.m02(); + double rm10 = right.m10(), rm11 = right.m11(), rm12 = right.m12(); + double rm20 = right.m20(), rm21 = right.m21(), rm22 = right.m22(); + double rm30 = right.m30(), rm31 = right.m31(), rm32 = right.m32(); + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30)))) + ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31)))) + ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32)))) + ._properties(properties & right.properties() & PROPERTY_ORTHONORMAL); + } + + public Matrix4x3d mulTranslation(Matrix4x3dc right, Matrix4x3d dest) { + return dest + ._m00(right.m00()) + ._m01(right.m01()) + ._m02(right.m02()) + ._m10(right.m10()) + ._m11(right.m11()) + ._m12(right.m12()) + ._m20(right.m20()) + ._m21(right.m21()) + ._m22(right.m22()) + ._m30(right.m30() + m30) + ._m31(right.m31() + m31) + ._m32(right.m32() + m32) + ._properties(right.properties() & PROPERTY_ORTHONORMAL); + } + + public Matrix4x3d mulTranslation(Matrix4x3fc right, Matrix4x3d dest) { + return dest + ._m00(right.m00()) + ._m01(right.m01()) + ._m02(right.m02()) + ._m10(right.m10()) + ._m11(right.m11()) + ._m12(right.m12()) + ._m20(right.m20()) + ._m21(right.m21()) + ._m22(right.m22()) + ._m30(right.m30() + m30) + ._m31(right.m31() + m31) + ._m32(right.m32() + m32) + ._properties(right.properties() & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this orthographic projection matrix by the supplied view matrix. + *

+ * If M is this matrix and V the view matrix, + * then the new matrix will be M * V. So when transforming a + * vector v with the new matrix by using M * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the matrix which to multiply this with + * @return this + */ + public Matrix4x3d mulOrtho(Matrix4x3dc view) { + return mulOrtho(view, this); + } + + public Matrix4x3d mulOrtho(Matrix4x3dc view, Matrix4x3d dest) { + double nm00 = m00 * view.m00(); + double nm01 = m11 * view.m01(); + double nm02 = m22 * view.m02(); + double nm10 = m00 * view.m10(); + double nm11 = m11 * view.m11(); + double nm12 = m22 * view.m12(); + double nm20 = m00 * view.m20(); + double nm21 = m11 * view.m21(); + double nm22 = m22 * view.m22(); + double nm30 = m00 * view.m30() + m30; + double nm31 = m11 * view.m31() + m31; + double nm32 = m22 * view.m32() + m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = (this.properties & view.properties() & PROPERTY_ORTHONORMAL); + return dest; + } + + /** + * Multiply this by the 4x3 matrix with the column vectors (rm00, rm01, rm02), + * (rm10, rm11, rm12), (rm20, rm21, rm22) and (0, 0, 0). + *

+ * If M is this matrix and R the specified matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the R matrix will be applied first! + * + * @param rm00 + * the value of the m00 element + * @param rm01 + * the value of the m01 element + * @param rm02 + * the value of the m02 element + * @param rm10 + * the value of the m10 element + * @param rm11 + * the value of the m11 element + * @param rm12 + * the value of the m12 element + * @param rm20 + * the value of the m20 element + * @param rm21 + * the value of the m21 element + * @param rm22 + * the value of the m22 element + * @return this + */ + public Matrix4x3d mul3x3( + double rm00, double rm01, double rm02, + double rm10, double rm11, double rm12, + double rm20, double rm21, double rm22) { + return mul3x3(rm00, rm01, rm02, rm10, rm11, rm12, rm20, rm21, rm22, this); + } + public Matrix4x3d mul3x3( + double rm00, double rm01, double rm02, + double rm10, double rm11, double rm12, + double rm20, double rm21, double rm22, + Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + double m20 = this.m20, m21 = this.m21, m22 = this.m22; + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._properties(0); + } + + /** + * Component-wise add this and other + * by first multiplying each component of other by otherFactor and + * adding that result to this. + *

+ * The matrix other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's components + * @return this + */ + public Matrix4x3d fma(Matrix4x3dc other, double otherFactor) { + return fma(other, otherFactor, this); + } + + public Matrix4x3d fma(Matrix4x3dc other, double otherFactor, Matrix4x3d dest) { + dest + ._m00(Math.fma(other.m00(), otherFactor, m00)) + ._m01(Math.fma(other.m01(), otherFactor, m01)) + ._m02(Math.fma(other.m02(), otherFactor, m02)) + ._m10(Math.fma(other.m10(), otherFactor, m10)) + ._m11(Math.fma(other.m11(), otherFactor, m11)) + ._m12(Math.fma(other.m12(), otherFactor, m12)) + ._m20(Math.fma(other.m20(), otherFactor, m20)) + ._m21(Math.fma(other.m21(), otherFactor, m21)) + ._m22(Math.fma(other.m22(), otherFactor, m22)) + ._m30(Math.fma(other.m30(), otherFactor, m30)) + ._m31(Math.fma(other.m31(), otherFactor, m31)) + ._m32(Math.fma(other.m32(), otherFactor, m32)) + ._properties(0); + return dest; + } + + /** + * Component-wise add this and other + * by first multiplying each component of other by otherFactor and + * adding that result to this. + *

+ * The matrix other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's components + * @return this + */ + public Matrix4x3d fma(Matrix4x3fc other, double otherFactor) { + return fma(other, otherFactor, this); + } + + public Matrix4x3d fma(Matrix4x3fc other, double otherFactor, Matrix4x3d dest) { + dest + ._m00(Math.fma(other.m00(), otherFactor, m00)) + ._m01(Math.fma(other.m01(), otherFactor, m01)) + ._m02(Math.fma(other.m02(), otherFactor, m02)) + ._m10(Math.fma(other.m10(), otherFactor, m10)) + ._m11(Math.fma(other.m11(), otherFactor, m11)) + ._m12(Math.fma(other.m12(), otherFactor, m12)) + ._m20(Math.fma(other.m20(), otherFactor, m20)) + ._m21(Math.fma(other.m21(), otherFactor, m21)) + ._m22(Math.fma(other.m22(), otherFactor, m22)) + ._m30(Math.fma(other.m30(), otherFactor, m30)) + ._m31(Math.fma(other.m31(), otherFactor, m31)) + ._m32(Math.fma(other.m32(), otherFactor, m32)) + ._properties(0); + return dest; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix4x3d add(Matrix4x3dc other) { + return add(other, this); + } + + public Matrix4x3d add(Matrix4x3dc other, Matrix4x3d dest) { + dest.m00 = m00 + other.m00(); + dest.m01 = m01 + other.m01(); + dest.m02 = m02 + other.m02(); + dest.m10 = m10 + other.m10(); + dest.m11 = m11 + other.m11(); + dest.m12 = m12 + other.m12(); + dest.m20 = m20 + other.m20(); + dest.m21 = m21 + other.m21(); + dest.m22 = m22 + other.m22(); + dest.m30 = m30 + other.m30(); + dest.m31 = m31 + other.m31(); + dest.m32 = m32 + other.m32(); + dest.properties = 0; + return dest; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix4x3d add(Matrix4x3fc other) { + return add(other, this); + } + + public Matrix4x3d add(Matrix4x3fc other, Matrix4x3d dest) { + dest.m00 = m00 + other.m00(); + dest.m01 = m01 + other.m01(); + dest.m02 = m02 + other.m02(); + dest.m10 = m10 + other.m10(); + dest.m11 = m11 + other.m11(); + dest.m12 = m12 + other.m12(); + dest.m20 = m20 + other.m20(); + dest.m21 = m21 + other.m21(); + dest.m22 = m22 + other.m22(); + dest.m30 = m30 + other.m30(); + dest.m31 = m31 + other.m31(); + dest.m32 = m32 + other.m32(); + dest.properties = 0; + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix4x3d sub(Matrix4x3dc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix4x3d sub(Matrix4x3dc subtrahend, Matrix4x3d dest) { + dest.m00 = m00 - subtrahend.m00(); + dest.m01 = m01 - subtrahend.m01(); + dest.m02 = m02 - subtrahend.m02(); + dest.m10 = m10 - subtrahend.m10(); + dest.m11 = m11 - subtrahend.m11(); + dest.m12 = m12 - subtrahend.m12(); + dest.m20 = m20 - subtrahend.m20(); + dest.m21 = m21 - subtrahend.m21(); + dest.m22 = m22 - subtrahend.m22(); + dest.m30 = m30 - subtrahend.m30(); + dest.m31 = m31 - subtrahend.m31(); + dest.m32 = m32 - subtrahend.m32(); + dest.properties = 0; + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix4x3d sub(Matrix4x3fc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix4x3d sub(Matrix4x3fc subtrahend, Matrix4x3d dest) { + dest.m00 = m00 - subtrahend.m00(); + dest.m01 = m01 - subtrahend.m01(); + dest.m02 = m02 - subtrahend.m02(); + dest.m10 = m10 - subtrahend.m10(); + dest.m11 = m11 - subtrahend.m11(); + dest.m12 = m12 - subtrahend.m12(); + dest.m20 = m20 - subtrahend.m20(); + dest.m21 = m21 - subtrahend.m21(); + dest.m22 = m22 - subtrahend.m22(); + dest.m30 = m30 - subtrahend.m30(); + dest.m31 = m31 - subtrahend.m31(); + dest.m32 = m32 - subtrahend.m32(); + dest.properties = 0; + return dest; + } + + /** + * Component-wise multiply this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix4x3d mulComponentWise(Matrix4x3dc other) { + return mulComponentWise(other, this); + } + + public Matrix4x3d mulComponentWise(Matrix4x3dc other, Matrix4x3d dest) { + dest.m00 = m00 * other.m00(); + dest.m01 = m01 * other.m01(); + dest.m02 = m02 * other.m02(); + dest.m10 = m10 * other.m10(); + dest.m11 = m11 * other.m11(); + dest.m12 = m12 * other.m12(); + dest.m20 = m20 * other.m20(); + dest.m21 = m21 * other.m21(); + dest.m22 = m22 * other.m22(); + dest.m30 = m30 * other.m30(); + dest.m31 = m31 * other.m31(); + dest.m32 = m32 * other.m32(); + dest.properties = 0; + return dest; + } + + /** + * Set the values within this matrix to the supplied double values. The matrix will look like this:

+ * + * m00, m10, m20, m30
+ * m01, m11, m21, m31
+ * m02, m12, m22, m32
+ * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m02 + * the new value of m02 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @param m12 + * the new value of m12 + * @param m20 + * the new value of m20 + * @param m21 + * the new value of m21 + * @param m22 + * the new value of m22 + * @param m30 + * the new value of m30 + * @param m31 + * the new value of m31 + * @param m32 + * the new value of m32 + * @return this + */ + public Matrix4x3d set(double m00, double m01, double m02, + double m10, double m11, double m12, + double m20, double m21, double m22, + double m30, double m31, double m32) { + this.m00 = m00; + this.m10 = m10; + this.m20 = m20; + this.m30 = m30; + this.m01 = m01; + this.m11 = m11; + this.m21 = m21; + this.m31 = m31; + this.m02 = m02; + this.m12 = m12; + this.m22 = m22; + this.m32 = m32; + return determineProperties(); + } + + /** + * Set the values in the matrix using a double array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 3, 6, 9
+ * 1, 4, 7, 10
+ * 2, 5, 8, 11
+ * + * @see #set(double[]) + * + * @param m + * the array to read the matrix values from + * @param off + * the offset into the array + * @return this + */ + public Matrix4x3d set(double m[], int off) { + m00 = m[off+0]; + m01 = m[off+1]; + m02 = m[off+2]; + m10 = m[off+3]; + m11 = m[off+4]; + m12 = m[off+5]; + m20 = m[off+6]; + m21 = m[off+7]; + m22 = m[off+8]; + m30 = m[off+9]; + m31 = m[off+10]; + m32 = m[off+11]; + return determineProperties(); + } + + /** + * Set the values in the matrix using a double array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 3, 6, 9
+ * 1, 4, 7, 10
+ * 2, 5, 8, 11
+ * + * @see #set(double[], int) + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix4x3d set(double m[]) { + return set(m, 0); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 3, 6, 9
+ * 1, 4, 7, 10
+ * 2, 5, 8, 11
+ * + * @see #set(float[]) + * + * @param m + * the array to read the matrix values from + * @param off + * the offset into the array + * @return this + */ + public Matrix4x3d set(float m[], int off) { + m00 = m[off+0]; + m01 = m[off+1]; + m02 = m[off+2]; + m10 = m[off+3]; + m11 = m[off+4]; + m12 = m[off+5]; + m20 = m[off+6]; + m21 = m[off+7]; + m22 = m[off+8]; + m30 = m[off+9]; + m31 = m[off+10]; + m32 = m[off+11]; + return determineProperties(); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 3, 6, 9
+ * 1, 4, 7, 10
+ * 2, 5, 8, 11
+ * + * @see #set(float[], int) + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix4x3d set(float m[]) { + return set(m, 0); + } + + /** + * Set the values of this matrix by reading 12 double values from the given {@link DoubleBuffer} in column-major order, + * starting at its current position. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d set(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 float values from the given {@link FloatBuffer} in column-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d set(FloatBuffer buffer) { + MemUtil.INSTANCE.getf(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 double values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 double values from the given {@link DoubleBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The DoubleBuffer is expected to contain the values in column-major order. + *

+ * The position of the DoubleBuffer will not be changed by this method. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * the DoubleBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d set(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 float values from the given {@link FloatBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.getf(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 double values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 float values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d setFloats(ByteBuffer buffer) { + MemUtil.INSTANCE.getf(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 float values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d setFloats(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.getf(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 double values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3d setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return determineProperties(); + } + + public double determinant() { + return (m00 * m11 - m01 * m10) * m22 + + (m02 * m10 - m00 * m12) * m21 + + (m01 * m12 - m02 * m11) * m20; + } + + /** + * Invert this matrix. + * + * @return this + */ + public Matrix4x3d invert() { + return invert(this); + } + + public Matrix4x3d invert(Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return invertOrthonormal(dest); + return invertGeneric(dest); + } + private Matrix4x3d invertGeneric(Matrix4x3d dest) { + double m11m00 = m00 * m11, m10m01 = m01 * m10, m10m02 = m02 * m10; + double m12m00 = m00 * m12, m12m01 = m01 * m12, m11m02 = m02 * m11; + double s = 1.0 / ((m11m00 - m10m01) * m22 + (m10m02 - m12m00) * m21 + (m12m01 - m11m02) * m20); + double m10m22 = m10 * m22, m10m21 = m10 * m21, m11m22 = m11 * m22; + double m11m20 = m11 * m20, m12m21 = m12 * m21, m12m20 = m12 * m20; + double m20m02 = m20 * m02, m20m01 = m20 * m01, m21m02 = m21 * m02; + double m21m00 = m21 * m00, m22m01 = m22 * m01, m22m00 = m22 * m00; + double nm00 = (m11m22 - m12m21) * s; + double nm01 = (m21m02 - m22m01) * s; + double nm02 = (m12m01 - m11m02) * s; + double nm10 = (m12m20 - m10m22) * s; + double nm11 = (m22m00 - m20m02) * s; + double nm12 = (m10m02 - m12m00) * s; + double nm20 = (m10m21 - m11m20) * s; + double nm21 = (m20m01 - m21m00) * s; + double nm22 = (m11m00 - m10m01) * s; + double nm30 = (m10m22 * m31 - m10m21 * m32 + m11m20 * m32 - m11m22 * m30 + m12m21 * m30 - m12m20 * m31) * s; + double nm31 = (m20m02 * m31 - m20m01 * m32 + m21m00 * m32 - m21m02 * m30 + m22m01 * m30 - m22m00 * m31) * s; + double nm32 = (m11m02 * m30 - m12m01 * m30 + m12m00 * m31 - m10m02 * m31 + m10m01 * m32 - m11m00 * m32) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = 0; + return dest; + } + private Matrix4x3d invertOrthonormal(Matrix4x3d dest) { + double nm30 = -(m00 * m30 + m01 * m31 + m02 * m32); + double nm31 = -(m10 * m30 + m11 * m31 + m12 * m32); + double nm32 = -(m20 * m30 + m21 * m31 + m22 * m32); + double m01 = this.m01; + double m02 = this.m02; + double m12 = this.m12; + dest.m00 = m00; + dest.m01 = m10; + dest.m02 = m20; + dest.m10 = m01; + dest.m11 = m11; + dest.m12 = m21; + dest.m20 = m02; + dest.m21 = m12; + dest.m22 = m22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = PROPERTY_ORTHONORMAL; + return dest; + } + + public Matrix4x3d invertOrtho(Matrix4x3d dest) { + double invM00 = 1.0 / m00; + double invM11 = 1.0 / m11; + double invM22 = 1.0 / m22; + dest.set(invM00, 0, 0, + 0, invM11, 0, + 0, 0, invM22, + -m30 * invM00, -m31 * invM11, -m32 * invM22); + dest.properties = 0; + return dest; + } + + /** + * Invert this orthographic projection matrix. + *

+ * This method can be used to quickly obtain the inverse of an orthographic projection matrix. + * + * @return this + */ + public Matrix4x3d invertOrtho() { + return invertOrtho(this); + } + + /** + * Transpose only the left 3x3 submatrix of this matrix and set the rest of the matrix elements to identity. + * + * @return this + */ + public Matrix4x3d transpose3x3() { + return transpose3x3(this); + } + + public Matrix4x3d transpose3x3(Matrix4x3d dest) { + double nm00 = m00; + double nm01 = m10; + double nm02 = m20; + double nm10 = m01; + double nm11 = m11; + double nm12 = m21; + double nm20 = m02; + double nm21 = m12; + double nm22 = m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.properties = properties; + return dest; + } + + public Matrix3d transpose3x3(Matrix3d dest) { + dest.m00(m00); + dest.m01(m10); + dest.m02(m20); + dest.m10(m01); + dest.m11(m11); + dest.m12(m21); + dest.m20(m02); + dest.m21(m12); + dest.m22(m22); + return dest; + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4x3d translation(double x, double y, double z) { + if ((properties & PROPERTY_IDENTITY) == 0) + this.identity(); + m30 = x; + m31 = y; + m32 = z; + properties = PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + * + * @param offset + * the offsets in x, y and z to translate + * @return this + */ + public Matrix4x3d translation(Vector3fc offset) { + return translation(offset.x(), offset.y(), offset.z()); + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + * + * @param offset + * the offsets in x, y and z to translate + * @return this + */ + public Matrix4x3d translation(Vector3dc offset) { + return translation(offset.x(), offset.y(), offset.z()); + } + + /** + * Set only the translation components (m30, m31, m32) of this matrix to the given values (x, y, z). + *

+ * To build a translation matrix instead, use {@link #translation(double, double, double)}. + * To apply a translation, use {@link #translate(double, double, double)}. + * + * @see #translation(double, double, double) + * @see #translate(double, double, double) + * + * @param x + * the units to translate in x + * @param y + * the units to translate in y + * @param z + * the units to translate in z + * @return this + */ + public Matrix4x3d setTranslation(double x, double y, double z) { + m30 = x; + m31 = y; + m32 = z; + properties &= ~(PROPERTY_IDENTITY); + return this; + } + + /** + * Set only the translation components (m30, m31, m32) of this matrix to the given values (xyz.x, xyz.y, xyz.z). + *

+ * To build a translation matrix instead, use {@link #translation(Vector3dc)}. + * To apply a translation, use {@link #translate(Vector3dc)}. + * + * @see #translation(Vector3dc) + * @see #translate(Vector3dc) + * + * @param xyz + * the units to translate in (x, y, z) + * @return this + */ + public Matrix4x3d setTranslation(Vector3dc xyz) { + return setTranslation(xyz.x(), xyz.y(), xyz.z()); + } + + public Vector3d getTranslation(Vector3d dest) { + dest.x = m30; + dest.y = m31; + dest.z = m32; + return dest; + } + + public Vector3d getScale(Vector3d dest) { + dest.x = Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02); + dest.y = Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12); + dest.z = Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22); + return dest; + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + " " + Runtime.format(m20, formatter) + " " + Runtime.format(m30, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + " " + Runtime.format(m21, formatter) + " " + Runtime.format(m31, formatter) + "\n" + + Runtime.format(m02, formatter) + " " + Runtime.format(m12, formatter) + " " + Runtime.format(m22, formatter) + " " + Runtime.format(m32, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix4x3dc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix4x3dc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix4x3d get(Matrix4x3d dest) { + return dest.set(this); + } + + public Quaternionf getUnnormalizedRotation(Quaternionf dest) { + return dest.setFromUnnormalized(this); + } + + public Quaternionf getNormalizedRotation(Quaternionf dest) { + return dest.setFromNormalized(this); + } + + public Quaterniond getUnnormalizedRotation(Quaterniond dest) { + return dest.setFromUnnormalized(this); + } + + public Quaterniond getNormalizedRotation(Quaterniond dest) { + return dest.setFromNormalized(this); + } + + public DoubleBuffer get(DoubleBuffer buffer) { + return get(buffer.position(), buffer); + } + + public DoubleBuffer get(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public FloatBuffer get(FloatBuffer buffer) { + return get(buffer.position(), buffer); + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.putf(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer getFloats(ByteBuffer buffer) { + return getFloats(buffer.position(), buffer); + } + + public ByteBuffer getFloats(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putf(this, index, buffer); + return buffer; + } + public Matrix4x3dc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + public double[] get(double[] arr, int offset) { + arr[offset+0] = m00; + arr[offset+1] = m01; + arr[offset+2] = m02; + arr[offset+3] = m10; + arr[offset+4] = m11; + arr[offset+5] = m12; + arr[offset+6] = m20; + arr[offset+7] = m21; + arr[offset+8] = m22; + arr[offset+9] = m30; + arr[offset+10] = m31; + arr[offset+11] = m32; + return arr; + } + + public double[] get(double[] arr) { + return get(arr, 0); + } + + public float[] get(float[] arr, int offset) { + arr[offset+0] = (float)m00; + arr[offset+1] = (float)m01; + arr[offset+2] = (float)m02; + arr[offset+3] = (float)m10; + arr[offset+4] = (float)m11; + arr[offset+5] = (float)m12; + arr[offset+6] = (float)m20; + arr[offset+7] = (float)m21; + arr[offset+8] = (float)m22; + arr[offset+9] = (float)m30; + arr[offset+10] = (float)m31; + arr[offset+11] = (float)m32; + return arr; + } + + public float[] get(float[] arr) { + return get(arr, 0); + } + + public float[] get4x4(float[] arr, int offset) { + MemUtil.INSTANCE.copy4x4(this, arr, offset); + return arr; + } + + public float[] get4x4(float[] arr) { + return get4x4(arr, 0); + } + + public double[] get4x4(double[] arr, int offset) { + MemUtil.INSTANCE.copy4x4(this, arr, offset); + return arr; + } + + public double[] get4x4(double[] arr) { + return get4x4(arr, 0); + } + + public DoubleBuffer get4x4(DoubleBuffer buffer) { + return get4x4(buffer.position(), buffer); + } + + public DoubleBuffer get4x4(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, index, buffer); + return buffer; + } + + public ByteBuffer get4x4(ByteBuffer buffer) { + return get4x4(buffer.position(), buffer); + } + + public ByteBuffer get4x4(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, index, buffer); + return buffer; + } + + public DoubleBuffer getTransposed(DoubleBuffer buffer) { + return getTransposed(buffer.position(), buffer); + } + + public DoubleBuffer getTransposed(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public ByteBuffer getTransposed(ByteBuffer buffer) { + return getTransposed(buffer.position(), buffer); + } + + public ByteBuffer getTransposed(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public FloatBuffer getTransposed(FloatBuffer buffer) { + return getTransposed(buffer.position(), buffer); + } + + public FloatBuffer getTransposed(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.putfTransposed(this, index, buffer); + return buffer; + } + + public ByteBuffer getTransposedFloats(ByteBuffer buffer) { + return getTransposed(buffer.position(), buffer); + } + + public ByteBuffer getTransposedFloats(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putfTransposed(this, index, buffer); + return buffer; + } + + public double[] getTransposed(double[] arr, int offset) { + arr[offset+0] = m00; + arr[offset+1] = m10; + arr[offset+2] = m20; + arr[offset+3] = m30; + arr[offset+4] = m01; + arr[offset+5] = m11; + arr[offset+6] = m21; + arr[offset+7] = m31; + arr[offset+8] = m02; + arr[offset+9] = m12; + arr[offset+10] = m22; + arr[offset+11] = m32; + return arr; + } + + public double[] getTransposed(double[] arr) { + return getTransposed(arr, 0); + } + + /** + * Set all the values within this matrix to 0. + * + * @return this + */ + public Matrix4x3d zero() { + m00 = 0.0; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 0.0; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 0.0; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = 0; + return this; + } + + /** + * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(double) scale()} instead. + * + * @see #scale(double) + * + * @param factor + * the scale factor in x, y and z + * @return this + */ + public Matrix4x3d scaling(double factor) { + return scaling(factor, factor, factor); + } + + /** + * Set this matrix to be a simple scale matrix. + * + * @param x + * the scale in x + * @param y + * the scale in y + * @param z + * the scale in z + * @return this + */ + public Matrix4x3d scaling(double x, double y, double z) { + if ((properties & PROPERTY_IDENTITY) == 0) + this.identity(); + m00 = x; + m11 = y; + m22 = z; + boolean one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z); + properties = one ? PROPERTY_ORTHONORMAL : 0; + return this; + } + + /** + * Set this matrix to be a simple scale matrix which scales the base axes by + * xyz.x, xyz.y and xyz.z, respectively. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix use {@link #scale(Vector3dc) scale()} instead. + * + * @see #scale(Vector3dc) + * + * @param xyz + * the scale in x, y and z, respectively + * @return this + */ + public Matrix4x3d scaling(Vector3dc xyz) { + return scaling(xyz.x(), xyz.y(), xyz.z()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * From Wikipedia + * + * @param angle + * the angle in radians + * @param x + * the x-coordinate of the axis to rotate about + * @param y + * the y-coordinate of the axis to rotate about + * @param z + * the z-coordinate of the axis to rotate about + * @return this + */ + public Matrix4x3d rotation(double angle, double x, double y, double z) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotationX(x * angle); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotationY(y * angle); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotationZ(z * angle); + return rotationInternal(angle, x, y, z); + } + private Matrix4x3d rotationInternal(double angle, double x, double y, double z) { + double sin = Math.sin(angle); + double cos = Math.cosFromSin(sin, angle); + double C = 1.0 - cos; + double xy = x * y, xz = x * z, yz = y * z; + m00 = cos + x * x * C; + m01 = xy * C + z * sin; + m02 = xz * C - y * sin; + m10 = xy * C - z * sin; + m11 = cos + y * y * C; + m12 = yz * C + x * sin; + m20 = xz * C + y * sin; + m21 = yz * C - x * sin; + m22 = cos + z * z * C; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3d rotationX(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = 1.0; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = cos; + m12 = sin; + m20 = 0.0; + m21 = -sin; + m22 = cos; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3d rotationY(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = cos; + m01 = 0.0; + m02 = -sin; + m10 = 0.0; + m11 = 1.0; + m12 = 0.0; + m20 = sin; + m21 = 0.0; + m22 = cos; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3d rotationZ(double ang) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = cos; + m01 = sin; + m02 = 0.0; + m10 = -sin; + m11 = cos; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3d rotationXYZ(double angleX, double angleY, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + + // rotateX + double nm11 = cosX; + double nm12 = sinX; + double nm21 = m_sinX; + double nm22 = cosX; + // rotateY + double nm00 = cosY; + double nm01 = nm21 * m_sinY; + double nm02 = nm22 * m_sinY; + m20 = sinY; + m21 = nm21 * cosY; + m22 = nm22 * cosY; + // rotateZ + m00 = nm00 * cosZ; + m01 = nm01 * cosZ + nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ; + m11 = nm01 * m_sinZ + nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + // set last column to identity + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4x3d rotationZYX(double angleZ, double angleY, double angleX) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + + // rotateZ + double nm00 = cosZ; + double nm01 = sinZ; + double nm10 = m_sinZ; + double nm11 = cosZ; + // rotateY + double nm20 = nm00 * sinY; + double nm21 = nm01 * sinY; + double nm22 = cosY; + m00 = nm00 * cosY; + m01 = nm01 * cosY; + m02 = m_sinY; + // rotateX + m10 = nm10 * cosX + nm20 * sinX; + m11 = nm11 * cosX + nm21 * sinX; + m12 = nm22 * sinX; + m20 = nm10 * m_sinX + nm20 * cosX; + m21 = nm11 * m_sinX + nm21 * cosX; + m22 = nm22 * cosX; + // set last column to identity + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3d rotationYXZ(double angleY, double angleX, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm00 = cosY; + double nm02 = m_sinY; + double nm20 = sinY; + double nm22 = cosY; + // rotateX + double nm10 = nm20 * sinX; + double nm11 = cosX; + double nm12 = nm22 * sinX; + m20 = nm20 * cosX; + m21 = m_sinX; + m22 = nm22 * cosX; + // rotateZ + m00 = nm00 * cosZ + nm10 * sinZ; + m01 = nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ + nm10 * cosZ; + m11 = nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + // set last column to identity + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set only the left 3x3 submatrix of this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3d setRotationXYZ(double angleX, double angleY, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + + // rotateX + double nm11 = cosX; + double nm12 = sinX; + double nm21 = m_sinX; + double nm22 = cosX; + // rotateY + double nm00 = cosY; + double nm01 = nm21 * m_sinY; + double nm02 = nm22 * m_sinY; + m20 = sinY; + m21 = nm21 * cosY; + m22 = nm22 * cosY; + // rotateZ + m00 = nm00 * cosZ; + m01 = nm01 * cosZ + nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ; + m11 = nm01 * m_sinZ + nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set only the left 3x3 submatrix of this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4x3d setRotationZYX(double angleZ, double angleY, double angleX) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + + // rotateZ + double nm00 = cosZ; + double nm01 = sinZ; + double nm10 = m_sinZ; + double nm11 = cosZ; + // rotateY + double nm20 = nm00 * sinY; + double nm21 = nm01 * sinY; + double nm22 = cosY; + m00 = nm00 * cosY; + m01 = nm01 * cosY; + m02 = m_sinY; + // rotateX + m10 = nm10 * cosX + nm20 * sinX; + m11 = nm11 * cosX + nm21 * sinX; + m12 = nm22 * sinX; + m20 = nm10 * m_sinX + nm20 * cosX; + m21 = nm11 * m_sinX + nm21 * cosX; + m22 = nm22 * cosX; + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set only the left 3x3 submatrix of this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3d setRotationYXZ(double angleY, double angleX, double angleZ) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm00 = cosY; + double nm02 = m_sinY; + double nm20 = sinY; + double nm22 = cosY; + // rotateX + double nm10 = nm20 * sinX; + double nm11 = cosX; + double nm12 = nm22 * sinX; + m20 = nm20 * cosX; + m21 = m_sinX; + m22 = nm22 * cosX; + // rotateZ + m00 = nm00 * cosZ + nm10 * sinZ; + m01 = nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ + nm10 * cosZ; + m11 = nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about + * @return this + */ + public Matrix4x3d rotation(double angle, Vector3dc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about + * @return this + */ + public Matrix4x3d rotation(double angle, Vector3fc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + public Vector4d transform(Vector4d v) { + return v.mul(this); + } + + public Vector4d transform(Vector4dc v, Vector4d dest) { + return v.mul(this, dest); + } + + public Vector3d transformPosition(Vector3d v) { + v.set(m00 * v.x + m10 * v.y + m20 * v.z + m30, + m01 * v.x + m11 * v.y + m21 * v.z + m31, + m02 * v.x + m12 * v.y + m22 * v.z + m32); + return v; + } + + public Vector3d transformPosition(Vector3dc v, Vector3d dest) { + dest.set(m00 * v.x() + m10 * v.y() + m20 * v.z() + m30, + m01 * v.x() + m11 * v.y() + m21 * v.z() + m31, + m02 * v.x() + m12 * v.y() + m22 * v.z() + m32); + return dest; + } + + public Vector3d transformDirection(Vector3d v) { + v.set(m00 * v.x + m10 * v.y + m20 * v.z, + m01 * v.x + m11 * v.y + m21 * v.z, + m02 * v.x + m12 * v.y + m22 * v.z); + return v; + } + + public Vector3d transformDirection(Vector3dc v, Vector3d dest) { + dest.set(m00 * v.x() + m10 * v.y() + m20 * v.z(), + m01 * v.x() + m11 * v.y() + m21 * v.z(), + m02 * v.x() + m12 * v.y() + m22 * v.z()); + return dest; + } + + /** + * Set the left 3x3 submatrix of this {@link Matrix4x3d} to the given {@link Matrix3dc} and don't change the other elements. + * + * @param mat + * the 3x3 matrix + * @return this + */ + public Matrix4x3d set3x3(Matrix3dc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + properties = 0; + return this; + } + + /** + * Set the left 3x3 submatrix of this {@link Matrix4x3d} to the given {@link Matrix3fc} and don't change the other elements. + * + * @param mat + * the 3x3 matrix + * @return this + */ + public Matrix4x3d set3x3(Matrix3fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + properties = 0; + return this; + } + + public Matrix4x3d scale(Vector3dc xyz, Matrix4x3d dest) { + return scale(xyz.x(), xyz.y(), xyz.z(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @return this + */ + public Matrix4x3d scale(Vector3dc xyz) { + return scale(xyz.x(), xyz.y(), xyz.z(), this); + } + + public Matrix4x3d scale(double x, double y, double z, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.scaling(x, y, z); + return scaleGeneric(x, y, z, dest); + } + private Matrix4x3d scaleGeneric(double x, double y, double z, Matrix4x3d dest) { + dest.m00 = m00 * x; + dest.m01 = m01 * x; + dest.m02 = m02 * x; + dest.m10 = m10 * y; + dest.m11 = m11 * y; + dest.m12 = m12 * y; + dest.m20 = m20 * z; + dest.m21 = m21 * z; + dest.m22 = m22 * z; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix4x3d scale(double x, double y, double z) { + return scale(x, y, z, this); + } + + public Matrix4x3d scale(double xyz, Matrix4x3d dest) { + return scale(xyz, xyz, xyz, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(double, double, double) + * + * @param xyz + * the factor for all components + * @return this + */ + public Matrix4x3d scale(double xyz) { + return scale(xyz, xyz, xyz); + } + + public Matrix4x3d scaleXY(double x, double y, Matrix4x3d dest) { + return scale(x, y, 1.0, dest); + } + + /** + * Apply scaling to this matrix by scaling the X axis by x and the Y axis by y. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix4x3d scaleXY(double x, double y) { + return scale(x, y, 1.0); + } + + public Matrix4x3d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz, Matrix4x3d dest) { + double nm30 = m00 * ox + m10 * oy + m20 * oz + m30; + double nm31 = m01 * ox + m11 * oy + m21 * oz + m31; + double nm32 = m02 * ox + m12 * oy + m22 * oz + m32; + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + return dest + ._m00(m00 * sx) + ._m01(m01 * sx) + ._m02(m02 * sx) + ._m10(m10 * sy) + ._m11(m11 * sy) + ._m12(m12 * sy) + ._m20(m20 * sz) + ._m21(m21 * sz) + ._m22(m22 * sz) + ._m30(-dest.m00 * ox - dest.m10 * oy - dest.m20 * oz + nm30) + ._m31(-dest.m01 * ox - dest.m11 * oy - dest.m21 * oz + nm31) + ._m32(-dest.m02 * ox - dest.m12 * oy - dest.m22 * oz + nm32) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | (one ? 0 : PROPERTY_ORTHONORMAL))); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4x3d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz) { + return scaleAround(sx, sy, sz, ox, oy, oz, this); + } + + /** + * Apply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4x3d scaleAround(double factor, double ox, double oy, double oz) { + return scaleAround(factor, factor, factor, ox, oy, oz, this); + } + + public Matrix4x3d scaleAround(double factor, double ox, double oy, double oz, Matrix4x3d dest) { + return scaleAround(factor, factor, factor, ox, oy, oz, dest); + } + + public Matrix4x3d scaleLocal(double x, double y, double z, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.scaling(x, y, z); + + double nm00 = x * m00; + double nm01 = y * m01; + double nm02 = z * m02; + double nm10 = x * m10; + double nm11 = y * m11; + double nm12 = z * m12; + double nm20 = x * m20; + double nm21 = y * m21; + double nm22 = z * m22; + double nm30 = x * m30; + double nm31 = y * m31; + double nm32 = z * m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return dest; + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix4x3d scaleLocal(double x, double y, double z) { + return scaleLocal(x, y, z, this); + } + + public Matrix4x3d rotate(double ang, double x, double y, double z, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(ang, x, y, z); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(ang, x, y, z, dest); + return rotateGeneric(ang, x, y, z, dest); + } + private Matrix4x3d rotateGeneric(double ang, double x, double y, double z, Matrix4x3d dest) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotateX(x * ang, dest); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotateY(y * ang, dest); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotateZ(z * ang, dest); + return rotateGenericInternal(ang, x, y, z, dest); + } + private Matrix4x3d rotateGenericInternal(double ang, double x, double y, double z, Matrix4x3d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double rm00 = xx * C + c; + double rm01 = xy * C + z * s; + double rm02 = xz * C - y * s; + double rm10 = xy * C - z * s; + double rm11 = yy * C + c; + double rm12 = yz * C + x * s; + double rm20 = xz * C + y * s; + double rm21 = yz * C - x * s; + double rm22 = zz * C + c; + // add temporaries for dependent values + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + // set non-dependent values directly + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the given axis specified as x, y and z components. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle is in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4x3d rotate(double ang, double x, double y, double z) { + return rotate(ang, x, y, z, this); + } + + /** + * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateTranslation(double ang, double x, double y, double z, Matrix4x3d dest) { + double tx = m30, ty = m31, tz = m32; + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return dest.rotationX(x * ang).setTranslation(tx, ty, tz); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return dest.rotationY(y * ang).setTranslation(tx, ty, tz); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return dest.rotationZ(z * ang).setTranslation(tx, ty, tz); + return rotateTranslationInternal(ang, x, y, z, dest); + } + private Matrix4x3d rotateTranslationInternal(double ang, double x, double y, double z, Matrix4x3d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double rm00 = xx * C + c; + double rm01 = xy * C + z * s; + double rm02 = xz * C - y * s; + double rm10 = xy * C - z * s; + double rm11 = yy * C + c; + double rm12 = yz * C + x * s; + double rm20 = xz * C + y * s; + double rm21 = yz * C - x * s; + double rm22 = zz * C + c; + double nm00 = rm00; + double nm01 = rm01; + double nm02 = rm02; + double nm10 = rm10; + double nm11 = rm11; + double nm12 = rm12; + // set non-dependent values directly + dest.m20 = rm20; + dest.m21 = rm21; + dest.m22 = rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply the rotation transformation of the given {@link Quaterniondc} to this matrix while using (ox, oy, oz) as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4x3d rotateAround(Quaterniondc quat, double ox, double oy, double oz) { + return rotateAround(quat, ox, oy, oz, this); + } + + private Matrix4x3d rotateAroundAffine(Quaterniondc quat, double ox, double oy, double oz, Matrix4x3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = dxy - dzw; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + double tm30 = m00 * ox + m10 * oy + m20 * oz + m30; + double tm31 = m01 * ox + m11 * oy + m21 * oz + m31; + double tm32 = m02 * ox + m12 * oy + m22 * oz + m32; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30) + ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31) + ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + public Matrix4x3d rotateAround(Quaterniondc quat, double ox, double oy, double oz, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return rotationAround(quat, ox, oy, oz); + return rotateAroundAffine(quat, ox, oy, oz, dest); + } + + /** + * Set this matrix to a transformation composed of a rotation of the specified {@link Quaterniondc} while using (ox, oy, oz) as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4x3d rotationAround(Quaterniondc quat, double ox, double oy, double oz) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + this._m20(dyw + dxz); + this._m21(dyz - dxw); + this._m22(z2 - y2 - x2 + w2); + this._m00(w2 + x2 - z2 - y2); + this._m01(dxy + dzw); + this._m02(dxz - dyw); + this._m10(dxy - dzw); + this._m11(y2 - z2 + w2 - x2); + this._m12(dyz + dxw); + this._m30(-m00 * ox - m10 * oy - m20 * oz + ox); + this._m31(-m01 * ox - m11 * oy - m21 * oz + oy); + this._m32(-m02 * ox - m12 * oy - m22 * oz + oz); + this.properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateLocal(double ang, double x, double y, double z, Matrix4x3d dest) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotateLocalX(x * ang, dest); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotateLocalY(y * ang, dest); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotateLocalZ(z * ang, dest); + return rotateLocalInternal(ang, x, y, z, dest); + } + private Matrix4x3d rotateLocalInternal(double ang, double x, double y, double z, Matrix4x3d dest) { + double s = Math.sin(ang); + double c = Math.cosFromSin(s, ang); + double C = 1.0 - c; + double xx = x * x, xy = x * y, xz = x * z; + double yy = y * y, yz = y * z; + double zz = z * z; + double lm00 = xx * C + c; + double lm01 = xy * C + z * s; + double lm02 = xz * C - y * s; + double lm10 = xy * C - z * s; + double lm11 = yy * C + c; + double lm12 = yz * C + x * s; + double lm20 = xz * C + y * s; + double lm21 = yz * C - x * s; + double lm22 = zz * C + c; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(double, double, double, double) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(double, double, double, double) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4x3d rotateLocal(double ang, double x, double y, double z) { + return rotateLocal(ang, x, y, z, this); + } + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(double) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(double) + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateLocalX(double ang, Matrix4x3d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm01 = cos * m01 - sin * m02; + double nm02 = sin * m01 + cos * m02; + double nm11 = cos * m11 - sin * m12; + double nm12 = sin * m11 + cos * m12; + double nm21 = cos * m21 - sin * m22; + double nm22 = sin * m21 + cos * m22; + double nm31 = cos * m31 - sin * m32; + double nm32 = sin * m31 + cos * m32; + dest.m00 = m00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = m10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = m20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = m30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(double) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(double) + * + * @param ang + * the angle in radians to rotate about the X axis + * @return this + */ + public Matrix4x3d rotateLocalX(double ang) { + return rotateLocalX(ang, this); + } + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateLocalY(double ang, Matrix4x3d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm00 = cos * m00 + sin * m02; + double nm02 = -sin * m00 + cos * m02; + double nm10 = cos * m10 + sin * m12; + double nm12 = -sin * m10 + cos * m12; + double nm20 = cos * m20 + sin * m22; + double nm22 = -sin * m20 + cos * m22; + double nm30 = cos * m30 + sin * m32; + double nm32 = -sin * m30 + cos * m32; + dest.m00 = nm00; + dest.m01 = m01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = m11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = m21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = m31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @return this + */ + public Matrix4x3d rotateLocalY(double ang) { + return rotateLocalY(ang, this); + } + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(double) rotationZ()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationZ(double) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateLocalZ(double ang, Matrix4x3d dest) { + double sin = Math.sin(ang); + double cos = Math.cosFromSin(sin, ang); + double nm00 = cos * m00 - sin * m01; + double nm01 = sin * m00 + cos * m01; + double nm10 = cos * m10 - sin * m11; + double nm11 = sin * m10 + cos * m11; + double nm20 = cos * m20 - sin * m21; + double nm21 = sin * m20 + cos * m21; + double nm30 = cos * m30 - sin * m31; + double nm31 = sin * m30 + cos * m31; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = m02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = m12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = m22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(double) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(double) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @return this + */ + public Matrix4x3d rotateLocalZ(double ang) { + return rotateLocalZ(ang, this); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3dc)}. + * + * @see #translation(Vector3dc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4x3d translate(Vector3dc offset) { + return translate(offset.x(), offset.y(), offset.z()); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3dc)}. + * + * @see #translation(Vector3dc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d translate(Vector3dc offset, Matrix4x3d dest) { + return translate(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4x3d translate(Vector3fc offset) { + return translate(offset.x(), offset.y(), offset.z()); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d translate(Vector3fc offset, Matrix4x3d dest) { + return translate(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(double, double, double)}. + * + * @see #translation(double, double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d translate(double x, double y, double z, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.translation(x, y, z); + return translateGeneric(x, y, z, dest); + } + private Matrix4x3d translateGeneric(double x, double y, double z, Matrix4x3d dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m30 = m00 * x + m10 * y + m20 * z + m30; + dest.m31 = m01 * x + m11 * y + m21 * z + m31; + dest.m32 = m02 * x + m12 * y + m22 * z + m32; + dest.properties = properties & ~(PROPERTY_IDENTITY); + return dest; + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(double, double, double)}. + * + * @see #translation(double, double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4x3d translate(double x, double y, double z) { + if ((properties & PROPERTY_IDENTITY) != 0) + return translation(x, y, z); + Matrix4x3d c = this; + c.m30 = c.m00 * x + c.m10 * y + c.m20 * z + c.m30; + c.m31 = c.m01 * x + c.m11 * y + c.m21 * z + c.m31; + c.m32 = c.m02 * x + c.m12 * y + c.m22 * z + c.m32; + c.properties &= ~(PROPERTY_IDENTITY); + return this; + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4x3d translateLocal(Vector3fc offset) { + return translateLocal(offset.x(), offset.y(), offset.z()); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d translateLocal(Vector3fc offset, Matrix4x3d dest) { + return translateLocal(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3dc)}. + * + * @see #translation(Vector3dc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4x3d translateLocal(Vector3dc offset) { + return translateLocal(offset.x(), offset.y(), offset.z()); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3dc)}. + * + * @see #translation(Vector3dc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d translateLocal(Vector3dc offset, Matrix4x3d dest) { + return translateLocal(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(double, double, double)}. + * + * @see #translation(double, double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d translateLocal(double x, double y, double z, Matrix4x3d dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m30 = m30 + x; + dest.m31 = m31 + y; + dest.m32 = m32 + z; + dest.properties = properties & ~(PROPERTY_IDENTITY); + return dest; + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(double, double, double)}. + * + * @see #translation(double, double, double) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4x3d translateLocal(double x, double y, double z) { + return translateLocal(x, y, z, this); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(m00); + out.writeDouble(m01); + out.writeDouble(m02); + out.writeDouble(m10); + out.writeDouble(m11); + out.writeDouble(m12); + out.writeDouble(m20); + out.writeDouble(m21); + out.writeDouble(m22); + out.writeDouble(m30); + out.writeDouble(m31); + out.writeDouble(m32); + } + + public void readExternal(ObjectInput in) throws IOException { + m00 = in.readDouble(); + m01 = in.readDouble(); + m02 = in.readDouble(); + m10 = in.readDouble(); + m11 = in.readDouble(); + m12 = in.readDouble(); + m20 = in.readDouble(); + m21 = in.readDouble(); + m22 = in.readDouble(); + m30 = in.readDouble(); + m31 = in.readDouble(); + m32 = in.readDouble(); + determineProperties(); + } + + public Matrix4x3d rotateX(double ang, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationX(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double x = m30, y = m31, z = m32; + return dest.rotationX(ang).setTranslation(x, y, z); + } + return rotateXInternal(ang, dest); + } + private Matrix4x3d rotateXInternal(double ang, Matrix4x3d dest) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + double rm11 = cos; + double rm12 = sin; + double rm21 = -sin; + double rm22 = cos; + + // add temporaries for dependent values + double nm10 = m10 * rm11 + m20 * rm12; + double nm11 = m11 * rm11 + m21 * rm12; + double nm12 = m12 * rm11 + m22 * rm12; + // set non-dependent values directly + dest.m20 = m10 * rm21 + m20 * rm22; + dest.m21 = m11 * rm21 + m21 * rm22; + dest.m22 = m12 * rm21 + m22 * rm22; + // set other values + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3d rotateX(double ang) { + return rotateX(ang, this); + } + + public Matrix4x3d rotateY(double ang, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationY(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double x = m30, y = m31, z = m32; + return dest.rotationY(ang).setTranslation(x, y, z); + } + return rotateYInternal(ang, dest); + } + private Matrix4x3d rotateYInternal(double ang, Matrix4x3d dest) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + double rm00 = cos; + double rm02 = -sin; + double rm20 = sin; + double rm22 = cos; + + // add temporaries for dependent values + double nm00 = m00 * rm00 + m20 * rm02; + double nm01 = m01 * rm00 + m21 * rm02; + double nm02 = m02 * rm00 + m22 * rm02; + // set non-dependent values directly + dest.m20 = m00 * rm20 + m20 * rm22; + dest.m21 = m01 * rm20 + m21 * rm22; + dest.m22 = m02 * rm20 + m22 * rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3d rotateY(double ang) { + return rotateY(ang, this); + } + + public Matrix4x3d rotateZ(double ang, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationZ(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double x = m30, y = m31, z = m32; + return dest.rotationZ(ang).setTranslation(x, y, z); + } + return rotateZInternal(ang, dest); + } + private Matrix4x3d rotateZInternal(double ang, Matrix4x3d dest) { + double sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + double rm00 = cos; + double rm01 = sin; + double rm10 = -sin; + double rm11 = cos; + + // add temporaries for dependent values + double nm00 = m00 * rm00 + m10 * rm01; + double nm01 = m01 * rm00 + m11 * rm01; + double nm02 = m02 * rm00 + m12 * rm01; + // set non-dependent values directly + dest.m10 = m00 * rm10 + m10 * rm11; + dest.m11 = m01 * rm10 + m11 * rm11; + dest.m12 = m02 * rm10 + m12 * rm11; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3d rotateZ(double ang) { + return rotateZ(ang, this); + } + + /** + * Apply rotation of angles.x radians about the X axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angles.x).rotateY(angles.y).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4x3d rotateXYZ(Vector3d angles) { + return rotateXYZ(angles.x, angles.y, angles.z); + } + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3d rotateXYZ(double angleX, double angleY, double angleZ) { + return rotateXYZ(angleX, angleY, angleZ, this); + } + + public Matrix4x3d rotateXYZ(double angleX, double angleY, double angleZ, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationXYZ(angleX, angleY, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double tx = m30, ty = m31, tz = m32; + return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz); + } + return rotateXYZInternal(angleX, angleY, angleZ, dest); + } + private Matrix4x3d rotateXYZInternal(double angleX, double angleY, double angleZ, Matrix4x3d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinX = -sinX; + double m_sinY = -sinY; + double m_sinZ = -sinZ; + + // rotateX + double nm10 = m10 * cosX + m20 * sinX; + double nm11 = m11 * cosX + m21 * sinX; + double nm12 = m12 * cosX + m22 * sinX; + double nm20 = m10 * m_sinX + m20 * cosX; + double nm21 = m11 * m_sinX + m21 * cosX; + double nm22 = m12 * m_sinX + m22 * cosX; + // rotateY + double nm00 = m00 * cosY + nm20 * m_sinY; + double nm01 = m01 * cosY + nm21 * m_sinY; + double nm02 = m02 * cosY + nm22 * m_sinY; + dest.m20 = m00 * sinY + nm20 * cosY; + dest.m21 = m01 * sinY + nm21 * cosY; + dest.m22 = m02 * sinY + nm22 * cosY; + // rotateZ + dest.m00 = nm00 * cosZ + nm10 * sinZ; + dest.m01 = nm01 * cosZ + nm11 * sinZ; + dest.m02 = nm02 * cosZ + nm12 * sinZ; + dest.m10 = nm00 * m_sinZ + nm10 * cosZ; + dest.m11 = nm01 * m_sinZ + nm11 * cosZ; + dest.m12 = nm02 * m_sinZ + nm12 * cosZ; + // copy last column from 'this' + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation of angles.z radians about the Z axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.x radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angles.z).rotateY(angles.y).rotateX(angles.x) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4x3d rotateZYX(Vector3d angles) { + return rotateZYX(angles.z, angles.y, angles.x); + } + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4x3d rotateZYX(double angleZ, double angleY, double angleX) { + return rotateZYX(angleZ, angleY, angleX, this); + } + + public Matrix4x3d rotateZYX(double angleZ, double angleY, double angleX, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationZYX(angleZ, angleY, angleX); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double tx = m30, ty = m31, tz = m32; + return dest.rotationZYX(angleZ, angleY, angleX).setTranslation(tx, ty, tz); + } + return rotateZYXInternal(angleZ, angleY, angleX, dest); + } + private Matrix4x3d rotateZYXInternal(double angleZ, double angleY, double angleX, Matrix4x3d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinZ = -sinZ; + double m_sinY = -sinY; + double m_sinX = -sinX; + + // rotateZ + double nm00 = m00 * cosZ + m10 * sinZ; + double nm01 = m01 * cosZ + m11 * sinZ; + double nm02 = m02 * cosZ + m12 * sinZ; + double nm10 = m00 * m_sinZ + m10 * cosZ; + double nm11 = m01 * m_sinZ + m11 * cosZ; + double nm12 = m02 * m_sinZ + m12 * cosZ; + // rotateY + double nm20 = nm00 * sinY + m20 * cosY; + double nm21 = nm01 * sinY + m21 * cosY; + double nm22 = nm02 * sinY + m22 * cosY; + dest.m00 = nm00 * cosY + m20 * m_sinY; + dest.m01 = nm01 * cosY + m21 * m_sinY; + dest.m02 = nm02 * cosY + m22 * m_sinY; + // rotateX + dest.m10 = nm10 * cosX + nm20 * sinX; + dest.m11 = nm11 * cosX + nm21 * sinX; + dest.m12 = nm12 * cosX + nm22 * sinX; + dest.m20 = nm10 * m_sinX + nm20 * cosX; + dest.m21 = nm11 * m_sinX + nm21 * cosX; + dest.m22 = nm12 * m_sinX + nm22 * cosX; + // copy last column from 'this' + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation of angles.y radians about the Y axis, followed by a rotation of angles.x radians about the X axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4x3d rotateYXZ(Vector3d angles) { + return rotateYXZ(angles.y, angles.x, angles.z); + } + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3d rotateYXZ(double angleY, double angleX, double angleZ) { + return rotateYXZ(angleY, angleX, angleZ, this); + } + + public Matrix4x3d rotateYXZ(double angleY, double angleX, double angleZ, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationYXZ(angleY, angleX, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + double tx = m30, ty = m31, tz = m32; + return dest.rotationYXZ(angleY, angleX, angleZ).setTranslation(tx, ty, tz); + } + return rotateYXZInternal(angleY, angleX, angleZ, dest); + } + private Matrix4x3d rotateYXZInternal(double angleY, double angleX, double angleZ, Matrix4x3d dest) { + double sinX = Math.sin(angleX); + double cosX = Math.cosFromSin(sinX, angleX); + double sinY = Math.sin(angleY); + double cosY = Math.cosFromSin(sinY, angleY); + double sinZ = Math.sin(angleZ); + double cosZ = Math.cosFromSin(sinZ, angleZ); + double m_sinY = -sinY; + double m_sinX = -sinX; + double m_sinZ = -sinZ; + + // rotateY + double nm20 = m00 * sinY + m20 * cosY; + double nm21 = m01 * sinY + m21 * cosY; + double nm22 = m02 * sinY + m22 * cosY; + double nm00 = m00 * cosY + m20 * m_sinY; + double nm01 = m01 * cosY + m21 * m_sinY; + double nm02 = m02 * cosY + m22 * m_sinY; + // rotateX + double nm10 = m10 * cosX + nm20 * sinX; + double nm11 = m11 * cosX + nm21 * sinX; + double nm12 = m12 * cosX + nm22 * sinX; + dest.m20 = m10 * m_sinX + nm20 * cosX; + dest.m21 = m11 * m_sinX + nm21 * cosX; + dest.m22 = m12 * m_sinX + nm22 * cosX; + // rotateZ + dest.m00 = nm00 * cosZ + nm10 * sinZ; + dest.m01 = nm01 * cosZ + nm11 * sinZ; + dest.m02 = nm02 * cosZ + nm12 * sinZ; + dest.m10 = nm00 * m_sinZ + nm10 * cosZ; + dest.m11 = nm01 * m_sinZ + nm11 * cosZ; + dest.m12 = nm02 * m_sinZ + nm12 * cosZ; + // copy last column from 'this' + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4f}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4f) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4f) + * + * @param angleAxis + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix4x3d rotation(AxisAngle4f angleAxis) { + return rotation(angleAxis.angle, angleAxis.x, angleAxis.y, angleAxis.z); + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4d}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4d) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4d) + * + * @param angleAxis + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @return this + */ + public Matrix4x3d rotation(AxisAngle4d angleAxis) { + return rotation(angleAxis.angle, angleAxis.x, angleAxis.y, angleAxis.z); + } + + /** + * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaterniondc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaterniondc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix4x3d rotation(Quaterniondc quat) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw; + double xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz; + double yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz; + double xw = quat.x() * quat.w(), dxw = xw + xw; + _m00(w2 + x2 - z2 - y2); + _m01(dxy + dzw); + _m02(dxz - dyw); + _m10(dxy - dzw); + _m11(y2 - z2 + w2 - x2); + _m12(dyz + dxw); + _m20(dyw + dxz); + _m21(dyz - dxw); + _m22(z2 - y2 - x2 + w2); + _m30(0.0); + _m31(0.0); + _m32(0.0); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaternionfc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaternionfc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4x3d rotation(Quaternionfc quat) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw; + double xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz; + double yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz; + double xw = quat.x() * quat.w(), dxw = xw + xw; + _m00(w2 + x2 - z2 - y2); + _m01(dxy + dzw); + _m02(dxz - dyw); + _m10(dxy - dzw); + _m11(y2 - z2 + w2 - x2); + _m12(dyz + dxw); + _m20(dyw + dxz); + _m21(dyz - dxw); + _m22(z2 - y2 - x2 + w2); + _m30(0.0); + _m31(0.0); + _m32(0.0); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to T * R * S, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), and S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz). + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz) + * + * @see #translation(double, double, double) + * @see #rotate(Quaterniondc) + * @see #scale(double, double, double) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @return this + */ + public Matrix4x3d translationRotateScale(double tx, double ty, double tz, + double qx, double qy, double qz, double qw, + double sx, double sy, double sz) { + double dqx = qx + qx, dqy = qy + qy, dqz = qz + qz; + double q00 = dqx * qx; + double q11 = dqy * qy; + double q22 = dqz * qz; + double q01 = dqx * qy; + double q02 = dqx * qz; + double q03 = dqx * qw; + double q12 = dqy * qz; + double q13 = dqy * qw; + double q23 = dqz * qw; + m00 = sx - (q11 + q22) * sx; + m01 = (q01 + q23) * sx; + m02 = (q02 - q13) * sx; + m10 = (q01 - q23) * sy; + m11 = sy - (q22 + q00) * sy; + m12 = (q12 + q03) * sy; + m20 = (q02 + q13) * sz; + m21 = (q12 - q03) * sz; + m22 = sz - (q11 + q00) * sz; + m30 = tx; + m31 = ty; + m32 = tz; + properties = 0; + return this; + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaternionfc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4x3d translationRotateScale(Vector3fc translation, + Quaternionfc quat, + Vector3fc scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3dc) + * @see #rotate(Quaterniondc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4x3d translationRotateScale(Vector3dc translation, + Quaterniondc quat, + Vector3dc scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to T * R * S * M, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz). + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz).mul(m) + * + * @see #translation(double, double, double) + * @see #rotate(Quaterniondc) + * @see #scale(double, double, double) + * @see #mul(Matrix4x3dc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @param m + * the matrix to multiply by + * @return this + */ + public Matrix4x3d translationRotateScaleMul( + double tx, double ty, double tz, + double qx, double qy, double qz, double qw, + double sx, double sy, double sz, + Matrix4x3dc m) { + double dqx = qx + qx; + double dqy = qy + qy; + double dqz = qz + qz; + double q00 = dqx * qx; + double q11 = dqy * qy; + double q22 = dqz * qz; + double q01 = dqx * qy; + double q02 = dqx * qz; + double q03 = dqx * qw; + double q12 = dqy * qz; + double q13 = dqy * qw; + double q23 = dqz * qw; + double nm00 = sx - (q11 + q22) * sx; + double nm01 = (q01 + q23) * sx; + double nm02 = (q02 - q13) * sx; + double nm10 = (q01 - q23) * sy; + double nm11 = sy - (q22 + q00) * sy; + double nm12 = (q12 + q03) * sy; + double nm20 = (q02 + q13) * sz; + double nm21 = (q12 - q03) * sz; + double nm22 = sz - (q11 + q00) * sz; + double m00 = nm00 * m.m00() + nm10 * m.m01() + nm20 * m.m02(); + double m01 = nm01 * m.m00() + nm11 * m.m01() + nm21 * m.m02(); + m02 = nm02 * m.m00() + nm12 * m.m01() + nm22 * m.m02(); + this.m00 = m00; + this.m01 = m01; + double m10 = nm00 * m.m10() + nm10 * m.m11() + nm20 * m.m12(); + double m11 = nm01 * m.m10() + nm11 * m.m11() + nm21 * m.m12(); + m12 = nm02 * m.m10() + nm12 * m.m11() + nm22 * m.m12(); + this.m10 = m10; + this.m11 = m11; + double m20 = nm00 * m.m20() + nm10 * m.m21() + nm20 * m.m22(); + double m21 = nm01 * m.m20() + nm11 * m.m21() + nm21 * m.m22(); + m22 = nm02 * m.m20() + nm12 * m.m21() + nm22 * m.m22(); + this.m20 = m20; + this.m21 = m21; + double m30 = nm00 * m.m30() + nm10 * m.m31() + nm20 * m.m32() + tx; + double m31 = nm01 * m.m30() + nm11 * m.m31() + nm21 * m.m32() + ty; + m32 = nm02 * m.m30() + nm12 * m.m31() + nm22 * m.m32() + tz; + this.m30 = m30; + this.m31 = m31; + properties = 0; + return this; + } + + /** + * Set this matrix to T * R * S * M, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, S is a scaling transformation + * which scales the axes by scale. + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale).mul(m) + * + * @see #translation(Vector3dc) + * @see #rotate(Quaterniondc) + * @see #mul(Matrix4x3dc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @param m + * the matrix to multiply by + * @return this + */ + public Matrix4x3d translationRotateScaleMul(Vector3dc translation, Quaterniondc quat, Vector3dc scale, Matrix4x3dc m) { + return translationRotateScaleMul(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z(), m); + } + + /** + * Set this matrix to T * R, where T is a translation by the given (tx, ty, tz) and + * R is a rotation transformation specified by the given quaternion. + *

+ * When transforming a vector by the resulting matrix the rotation transformation will be applied first and then the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat) + * + * @see #translation(double, double, double) + * @see #rotate(Quaterniondc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param quat + * the quaternion representing a rotation + * @return this + */ + public Matrix4x3d translationRotate(double tx, double ty, double tz, Quaterniondc quat) { + double dqx = quat.x() + quat.x(), dqy = quat.y() + quat.y(), dqz = quat.z() + quat.z(); + double q00 = dqx * quat.x(); + double q11 = dqy * quat.y(); + double q22 = dqz * quat.z(); + double q01 = dqx * quat.y(); + double q02 = dqx * quat.z(); + double q03 = dqx * quat.w(); + double q12 = dqy * quat.z(); + double q13 = dqy * quat.w(); + double q23 = dqz * quat.w(); + m00 = 1.0 - (q11 + q22); + m01 = q01 + q23; + m02 = q02 - q13; + m10 = q01 - q23; + m11 = 1.0 - (q22 + q00); + m12 = q12 + q03; + m20 = q02 + q13; + m21 = q12 - q03; + m22 = 1.0 - (q11 + q00); + m30 = tx; + m31 = ty; + m32 = tz; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to T * R * M, where T is a translation by the given (tx, ty, tz), + * R is a rotation - and possibly scaling - transformation specified by the given quaternion and M is the given matrix mat. + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).mul(mat) + * + * @see #translation(double, double, double) + * @see #rotate(Quaternionfc) + * @see #mul(Matrix4x3dc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param quat + * the quaternion representing a rotation + * @param mat + * the matrix to multiply with + * @return this + */ + public Matrix4x3d translationRotateMul(double tx, double ty, double tz, Quaternionfc quat, Matrix4x3dc mat) { + return translationRotateMul(tx, ty, tz, quat.x(), quat.y(), quat.z(), quat.w(), mat); + } + + /** + * Set this matrix to T * R * M, where T is a translation by the given (tx, ty, tz), + * R is a rotation - and possibly scaling - transformation specified by the quaternion (qx, qy, qz, qw) and M is the given matrix mat + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).mul(mat) + * + * @see #translation(double, double, double) + * @see #rotate(Quaternionfc) + * @see #mul(Matrix4x3dc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param mat + * the matrix to multiply with + * @return this + */ + public Matrix4x3d translationRotateMul(double tx, double ty, double tz, double qx, double qy, double qz, double qw, Matrix4x3dc mat) { + double w2 = qw * qw; + double x2 = qx * qx; + double y2 = qy * qy; + double z2 = qz * qz; + double zw = qz * qw; + double xy = qx * qy; + double xz = qx * qz; + double yw = qy * qw; + double yz = qy * qz; + double xw = qx * qw; + double nm00 = w2 + x2 - z2 - y2; + double nm01 = xy + zw + zw + xy; + double nm02 = xz - yw + xz - yw; + double nm10 = -zw + xy - zw + xy; + double nm11 = y2 - z2 + w2 - x2; + double nm12 = yz + yz + xw + xw; + double nm20 = yw + xz + xz + yw; + double nm21 = yz + yz - xw - xw; + double nm22 = z2 - y2 - x2 + w2; + m00 = nm00 * mat.m00() + nm10 * mat.m01() + nm20 * mat.m02(); + m01 = nm01 * mat.m00() + nm11 * mat.m01() + nm21 * mat.m02(); + m02 = nm02 * mat.m00() + nm12 * mat.m01() + nm22 * mat.m02(); + m10 = nm00 * mat.m10() + nm10 * mat.m11() + nm20 * mat.m12(); + m11 = nm01 * mat.m10() + nm11 * mat.m11() + nm21 * mat.m12(); + m12 = nm02 * mat.m10() + nm12 * mat.m11() + nm22 * mat.m12(); + m20 = nm00 * mat.m20() + nm10 * mat.m21() + nm20 * mat.m22(); + m21 = nm01 * mat.m20() + nm11 * mat.m21() + nm21 * mat.m22(); + m22 = nm02 * mat.m20() + nm12 * mat.m21() + nm22 * mat.m22(); + m30 = nm00 * mat.m30() + nm10 * mat.m31() + nm20 * mat.m32() + tx; + m31 = nm01 * mat.m30() + nm11 * mat.m31() + nm21 * mat.m32() + ty; + m32 = nm02 * mat.m30() + nm12 * mat.m31() + nm22 * mat.m32() + tz; + this.properties = 0; + return this; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotate(Quaterniondc quat, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(quat); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(quat, dest); + return rotateGeneric(quat, dest); + } + private Matrix4x3d rotateGeneric(Quaterniondc quat, Matrix4x3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = dxy - dzw; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotate(Quaternionfc quat, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(quat); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(quat, dest); + return rotateGeneric(quat, dest); + } + private Matrix4x3d rotateGeneric(Quaternionfc quat, Matrix4x3d dest) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(); + double xy = quat.x() * quat.y(); + double xz = quat.x() * quat.z(); + double yw = quat.y() * quat.w(); + double yz = quat.y() * quat.z(); + double xw = quat.x() * quat.w(); + double rm00 = w2 + x2 - z2 - y2; + double rm01 = xy + zw + zw + xy; + double rm02 = xz - yw + xz - yw; + double rm10 = -zw + xy - zw + xy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = yz + yz + xw + xw; + double rm20 = yw + xz + xz + yw; + double rm21 = yz + yz - xw - xw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix4x3d rotate(Quaterniondc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4x3d rotate(Quaternionfc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateTranslation(Quaterniondc quat, Matrix4x3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double rm00 = w2 + x2 - z2 - y2; + double rm01 = dxy + dzw; + double rm02 = dxz - dyw; + double rm10 = dxy - dzw; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = dyz + dxw; + double rm20 = dyw + dxz; + double rm21 = dyz - dxw; + double rm22 = z2 - y2 - x2 + w2; + dest.m20 = rm20; + dest.m21 = rm21; + dest.m22 = rm22; + dest.m00 = rm00; + dest.m01 = rm01; + dest.m02 = rm02; + dest.m10 = rm10; + dest.m11 = rm11; + dest.m12 = rm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateTranslation(Quaternionfc quat, Matrix4x3d dest) { + double w2 = quat.w() * quat.w(); + double x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(); + double z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(); + double xy = quat.x() * quat.y(); + double xz = quat.x() * quat.z(); + double yw = quat.y() * quat.w(); + double yz = quat.y() * quat.z(); + double xw = quat.x() * quat.w(); + double rm00 = w2 + x2 - z2 - y2; + double rm01 = xy + zw + zw + xy; + double rm02 = xz - yw + xz - yw; + double rm10 = -zw + xy - zw + xy; + double rm11 = y2 - z2 + w2 - x2; + double rm12 = yz + yz + xw + xw; + double rm20 = yw + xz + xz + yw; + double rm21 = yz + yz - xw - xw; + double rm22 = z2 - y2 - x2 + w2; + double nm00 = rm00; + double nm01 = rm01; + double nm02 = rm02; + double nm10 = rm10; + double nm11 = rm11; + double nm12 = rm12; + dest.m20 = rm20; + dest.m21 = rm21; + dest.m22 = rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateLocal(Quaterniondc quat, Matrix4x3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double lm00 = w2 + x2 - z2 - y2; + double lm01 = dxy + dzw; + double lm02 = dxz - dyw; + double lm10 = dxy - dzw; + double lm11 = y2 - z2 + w2 - x2; + double lm12 = dyz + dxw; + double lm20 = dyw + dxz; + double lm21 = dyz - dxw; + double lm22 = z2 - y2 - x2 + w2; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply the rotation transformation of the given {@link Quaterniondc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaterniondc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaterniondc) + * + * @param quat + * the {@link Quaterniondc} + * @return this + */ + public Matrix4x3d rotateLocal(Quaterniondc quat) { + return rotateLocal(quat, this); + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateLocal(Quaternionfc quat, Matrix4x3d dest) { + double w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + double y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + double zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + double xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + double yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + double lm00 = w2 + x2 - z2 - y2; + double lm01 = dxy + dzw; + double lm02 = dxz - dyw; + double lm10 = dxy - dzw; + double lm11 = y2 - z2 + w2 - x2; + double lm12 = dyz + dxw; + double lm20 = dyw + dxz; + double lm21 = dyz - dxw; + double lm22 = z2 - y2 - x2 + w2; + double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4x3d rotateLocal(Quaternionfc quat) { + return rotateLocal(quat, this); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f}, to this matrix. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix4x3d rotate(AxisAngle4f axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotate(AxisAngle4f axisAngle, Matrix4x3d dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d}, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4d)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @return this + */ + public Matrix4x3d rotate(AxisAngle4d axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4d)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(AxisAngle4d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotate(AxisAngle4d axisAngle, Matrix4x3d dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3dc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3dc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @return this + */ + public Matrix4x3d rotate(double angle, Vector3dc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3dc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3dc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotate(double angle, Vector3dc axis, Matrix4x3d dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix4x3d rotate(double angle, Vector3fc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(double, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double) + * @see #rotation(double, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotate(double angle, Vector3fc axis, Matrix4x3d dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + public Vector4d getRow(int row, Vector4d dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + dest.x = m00; + dest.y = m10; + dest.z = m20; + dest.w = m30; + break; + case 1: + dest.x = m01; + dest.y = m11; + dest.z = m21; + dest.w = m31; + break; + case 2: + dest.x = m02; + dest.y = m12; + dest.z = m22; + dest.w = m32; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param src + * the row components to set + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + public Matrix4x3d setRow(int row, Vector4dc src) throws IndexOutOfBoundsException { + switch (row) { + case 0: + this.m00 = src.x(); + this.m10 = src.y(); + this.m20 = src.z(); + this.m30 = src.w(); + break; + case 1: + this.m01 = src.x(); + this.m11 = src.y(); + this.m21 = src.z(); + this.m31 = src.w(); + break; + case 2: + this.m02 = src.x(); + this.m12 = src.y(); + this.m22 = src.z(); + this.m32 = src.w(); + break; + default: + throw new IndexOutOfBoundsException(); + } + properties = 0; + return this; + } + + public Vector3d getColumn(int column, Vector3d dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + dest.x = m00; + dest.y = m01; + dest.z = m02; + break; + case 1: + dest.x = m10; + dest.y = m11; + dest.z = m12; + break; + case 2: + dest.x = m20; + dest.y = m21; + dest.z = m22; + break; + case 3: + dest.x = m30; + dest.y = m31; + dest.z = m32; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param src + * the column components to set + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + public Matrix4x3d setColumn(int column, Vector3dc src) throws IndexOutOfBoundsException { + switch (column) { + case 0: + this.m00 = src.x(); + this.m01 = src.y(); + this.m02 = src.z(); + break; + case 1: + this.m10 = src.x(); + this.m11 = src.y(); + this.m12 = src.z(); + break; + case 2: + this.m20 = src.x(); + this.m21 = src.y(); + this.m22 = src.z(); + break; + case 3: + this.m30 = src.x(); + this.m31 = src.y(); + this.m32 = src.z(); + break; + default: + throw new IndexOutOfBoundsException(); + } + properties = 0; + return this; + } + + /** + * Compute a normal matrix from the left 3x3 submatrix of this + * and store it into the left 3x3 submatrix of this. + * All other values of this will be set to {@link #identity() identity}. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link #set3x3(Matrix4x3dc)} to set a given Matrix4x3d to only the left 3x3 submatrix + * of this matrix. + * + * @see #set3x3(Matrix4x3dc) + * + * @return this + */ + public Matrix4x3d normal() { + return normal(this); + } + + /** + * Compute a normal matrix from the left 3x3 submatrix of this + * and store it into the left 3x3 submatrix of dest. + * All other values of dest will be set to {@link #identity() identity}. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link #set3x3(Matrix4x3dc)} to set a given Matrix4x3d to only the left 3x3 submatrix + * of a given matrix. + * + * @see #set3x3(Matrix4x3dc) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d normal(Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalOrthonormal(dest); + return normalGeneric(dest); + } + private Matrix4x3d normalOrthonormal(Matrix4x3d dest) { + if (dest != this) + dest.set(this); + return dest._properties(PROPERTY_ORTHONORMAL); + } + private Matrix4x3d normalGeneric(Matrix4x3d dest) { + double m00m11 = m00 * m11; + double m01m10 = m01 * m10; + double m02m10 = m02 * m10; + double m00m12 = m00 * m12; + double m01m12 = m01 * m12; + double m02m11 = m02 * m11; + double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + double s = 1.0 / det; + /* Invert and transpose in one go */ + double nm00 = (m11 * m22 - m21 * m12) * s; + double nm01 = (m20 * m12 - m10 * m22) * s; + double nm02 = (m10 * m21 - m20 * m11) * s; + double nm10 = (m21 * m02 - m01 * m22) * s; + double nm11 = (m00 * m22 - m20 * m02) * s; + double nm12 = (m20 * m01 - m00 * m21) * s; + double nm20 = (m01m12 - m02m11) * s; + double nm21 = (m02m10 - m00m12) * s; + double nm22 = (m00m11 - m01m10) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = 0.0; + dest.m31 = 0.0; + dest.m32 = 0.0; + dest.properties = properties & ~PROPERTY_TRANSLATION; + return dest; + } + + public Matrix3d normal(Matrix3d dest) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalOrthonormal(dest); + return normalGeneric(dest); + } + private Matrix3d normalOrthonormal(Matrix3d dest) { + return dest.set(this); + } + private Matrix3d normalGeneric(Matrix3d dest) { + double m00m11 = m00 * m11; + double m01m10 = m01 * m10; + double m02m10 = m02 * m10; + double m00m12 = m00 * m12; + double m01m12 = m01 * m12; + double m02m11 = m02 * m11; + double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + double s = 1.0 / det; + /* Invert and transpose in one go */ + dest.m00((m11 * m22 - m21 * m12) * s); + dest.m01((m20 * m12 - m10 * m22) * s); + dest.m02((m10 * m21 - m20 * m11) * s); + dest.m10((m21 * m02 - m01 * m22) * s); + dest.m11((m00 * m22 - m20 * m02) * s); + dest.m12((m20 * m01 - m00 * m21) * s); + dest.m20((m01m12 - m02m11) * s); + dest.m21((m02m10 - m00m12) * s); + dest.m22((m00m11 - m01m10) * s); + return dest; + } + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this. + *

+ * The cofactor matrix can be used instead of {@link #normal()} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @return this + */ + public Matrix4x3d cofactor3x3() { + return cofactor3x3(this); + } + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this + * and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3d cofactor3x3(Matrix3d dest) { + dest.m00 = m11 * m22 - m21 * m12; + dest.m01 = m20 * m12 - m10 * m22; + dest.m02 = m10 * m21 - m20 * m11; + dest.m10 = m21 * m02 - m01 * m22; + dest.m11 = m00 * m22 - m20 * m02; + dest.m12 = m20 * m01 - m00 * m21; + dest.m20 = m01 * m12 - m02 * m11; + dest.m21 = m02 * m10 - m00 * m12; + dest.m22 = m00 * m11 - m01 * m10; + return dest; + } + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this + * and store it into dest. + * All other values of dest will be set to {@link #identity() identity}. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix4x3d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d cofactor3x3(Matrix4x3d dest) { + double nm00 = m11 * m22 - m21 * m12; + double nm01 = m20 * m12 - m10 * m22; + double nm02 = m10 * m21 - m20 * m11; + double nm10 = m21 * m02 - m01 * m22; + double nm11 = m00 * m22 - m20 * m02; + double nm12 = m20 * m01 - m00 * m21; + double nm20 = m01 * m12 - m11 * m02; + double nm21 = m02 * m10 - m12 * m00; + double nm22 = m00 * m11 - m10 * m01; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = 0.0; + dest.m31 = 0.0; + dest.m32 = 0.0; + dest.properties = properties & ~PROPERTY_TRANSLATION; + return dest; + } + + /** + * Normalize the left 3x3 submatrix of this matrix. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @return this + */ + public Matrix4x3d normalize3x3() { + return normalize3x3(this); + } + + public Matrix4x3d normalize3x3(Matrix4x3d dest) { + double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + dest.m00 = m00 * invXlen; dest.m01 = m01 * invXlen; dest.m02 = m02 * invXlen; + dest.m10 = m10 * invYlen; dest.m11 = m11 * invYlen; dest.m12 = m12 * invYlen; + dest.m20 = m20 * invZlen; dest.m21 = m21 * invZlen; dest.m22 = m22 * invZlen; + return dest; + } + + public Matrix3d normalize3x3(Matrix3d dest) { + double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + dest.m00(m00 * invXlen); dest.m01(m01 * invXlen); dest.m02(m02 * invXlen); + dest.m10(m10 * invYlen); dest.m11(m11 * invYlen); dest.m12(m12 * invYlen); + dest.m20(m20 * invZlen); dest.m21(m21 * invZlen); dest.m22(m22 * invZlen); + return dest; + } + + public Matrix4x3d reflect(double a, double b, double c, double d, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.reflection(a, b, c, d); + + double da = a + a, db = b + b, dc = c + c, dd = d + d; + double rm00 = 1.0 - da * a; + double rm01 = -da * b; + double rm02 = -da * c; + double rm10 = -db * a; + double rm11 = 1.0 - db * b; + double rm12 = -db * c; + double rm20 = -dc * a; + double rm21 = -dc * b; + double rm22 = 1.0 - dc * c; + double rm30 = -dd * a; + double rm31 = -dd * b; + double rm32 = -dd * c; + + // matrix multiplication + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4x3d reflect(double a, double b, double c, double d) { + return reflect(a, b, c, d, this); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @return this + */ + public Matrix4x3d reflect(double nx, double ny, double nz, double px, double py, double pz) { + return reflect(nx, ny, nz, px, py, pz, this); + } + + public Matrix4x3d reflect(double nx, double ny, double nz, double px, double py, double pz, Matrix4x3d dest) { + double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz); + double nnx = nx * invLength; + double nny = ny * invLength; + double nnz = nz * invLength; + /* See: http://mathworld.wolfram.com/Plane.html */ + return reflect(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz, dest); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @return this + */ + public Matrix4x3d reflect(Vector3dc normal, Vector3dc point) { + return reflect(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z()); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation and a point on the plane. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation relative to an implied normal vector of (0, 0, 1) + * @param point + * a point on the plane + * @return this + */ + public Matrix4x3d reflect(Quaterniondc orientation, Vector3dc point) { + return reflect(orientation, point, this); + } + + public Matrix4x3d reflect(Quaterniondc orientation, Vector3dc point, Matrix4x3d dest) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + double normalX = orientation.x() * num3 + orientation.w() * num2; + double normalY = orientation.y() * num3 - orientation.w() * num1; + double normalZ = 1.0 - (orientation.x() * num1 + orientation.y() * num2); + return reflect(normalX, normalY, normalZ, point.x(), point.y(), point.z(), dest); + } + + public Matrix4x3d reflect(Vector3dc normal, Vector3dc point, Matrix4x3d dest) { + return reflect(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z(), dest); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4x3d reflection(double a, double b, double c, double d) { + double da = a + a, db = b + b, dc = c + c, dd = d + d; + m00 = 1.0 - da * a; + m01 = -da * b; + m02 = -da * c; + m10 = -db * a; + m11 = 1.0 - db * b; + m12 = -db * c; + m20 = -dc * a; + m21 = -dc * b; + m22 = 1.0 - dc * c; + m30 = -dd * a; + m31 = -dd * b; + m32 = -dd * c; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the plane normal and a point on the plane. + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @return this + */ + public Matrix4x3d reflection(double nx, double ny, double nz, double px, double py, double pz) { + double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz); + double nnx = nx * invLength; + double nny = ny * invLength; + double nnz = nz * invLength; + /* See: http://mathworld.wolfram.com/Plane.html */ + return reflection(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the plane normal and a point on the plane. + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @return this + */ + public Matrix4x3d reflection(Vector3dc normal, Vector3dc point) { + return reflection(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z()); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about a plane + * specified via the plane orientation and a point on the plane. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + * + * @param orientation + * the plane orientation + * @param point + * a point on the plane + * @return this + */ + public Matrix4x3d reflection(Quaterniondc orientation, Vector3dc point) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + double normalX = orientation.x() * num3 + orientation.w() * num2; + double normalY = orientation.y() * num3 - orientation.w() * num1; + double normalZ = 1.0 - (orientation.x() * num1 + orientation.y() * num2); + return reflection(normalX, normalY, normalZ, point.x(), point.y(), point.z()); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(double, double, double, double, double, double, boolean) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d ortho(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4x3d dest) { + // calculate right matrix elements + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar); + double rm30 = (left + right) / (left - right); + double rm31 = (top + bottom) / (bottom - top); + double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20 * rm22; + dest.m21 = m21 * rm22; + dest.m22 = m22 * rm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d ortho(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4x3d dest) { + return ortho(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(double, double, double, double, double, double, boolean) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3d ortho(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + return ortho(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3d ortho(double left, double right, double bottom, double top, double zNear, double zFar) { + return ortho(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(double, double, double, double, double, double, boolean) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4x3d dest) { + // calculate right matrix elements + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear); + double rm30 = (left + right) / (left - right); + double rm31 = (top + bottom) / (bottom - top); + double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20 * rm22; + dest.m21 = m21 * rm22; + dest.m22 = m22 * rm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4x3d dest) { + return orthoLH(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(double, double, double, double, double, double, boolean) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + return orthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar) { + return orthoLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho(double, double, double, double, double, double, boolean) ortho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + m00 = 2.0 / (right - left); + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 2.0 / (top - bottom); + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar); + m30 = (right + left) / (left - right); + m31 = (top + bottom) / (bottom - top); + m32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + properties = 0; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho(double, double, double, double, double, double) ortho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar) { + return setOrtho(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #orthoLH(double, double, double, double, double, double, boolean) orthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne) { + m00 = 2.0 / (right - left); + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 2.0 / (top - bottom); + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear); + m30 = (right + left) / (left - right); + m31 = (top + bottom) / (bottom - top); + m32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + properties = 0; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #orthoLH(double, double, double, double, double, double) orthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar) { + return setOrthoLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, boolean, Matrix4x3d) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(double, double, double, double, boolean) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4x3d orthoSymmetric(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4x3d dest) { + // calculate right matrix elements + double rm00 = 2.0 / width; + double rm11 = 2.0 / height; + double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar); + double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m20 * rm32 + m30; + dest.m31 = m21 * rm32 + m31; + dest.m32 = m22 * rm32 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20 * rm22; + dest.m21 = m21 * rm22; + dest.m22 = m22 * rm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4x3d) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d orthoSymmetric(double width, double height, double zNear, double zFar, Matrix4x3d dest) { + return orthoSymmetric(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, boolean) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(double, double, double, double, boolean) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3d orthoSymmetric(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + return orthoSymmetric(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3d orthoSymmetric(double width, double height, double zNear, double zFar) { + return orthoSymmetric(width, height, zNear, zFar, false, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, boolean, Matrix4x3d) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(double, double, double, double, boolean) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4x3d orthoSymmetricLH(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4x3d dest) { + // calculate right matrix elements + double rm00 = 2.0 / width; + double rm11 = 2.0 / height; + double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear); + double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m20 * rm32 + m30; + dest.m31 = m21 * rm32 + m31; + dest.m32 = m22 * rm32 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20 * rm22; + dest.m21 = m21 * rm22; + dest.m22 = m22 * rm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4x3d) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d orthoSymmetricLH(double width, double height, double zNear, double zFar, Matrix4x3d dest) { + return orthoSymmetricLH(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, boolean) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(double, double, double, double, boolean) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3d orthoSymmetricLH(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + return orthoSymmetricLH(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3d orthoSymmetricLH(double width, double height, double zNear, double zFar) { + return orthoSymmetricLH(width, height, zNear, zFar, false, this); + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, boolean) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetric(double, double, double, double, boolean) orthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetric(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3d setOrthoSymmetric(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + m00 = 2.0 / width; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 2.0 / height; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar); + m30 = 0.0; + m31 = 0.0; + m32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + properties = 0; + return this; + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetric(double, double, double, double) orthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetric(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3d setOrthoSymmetric(double width, double height, double zNear, double zFar) { + return setOrthoSymmetric(width, height, zNear, zFar, false); + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system using the given NDC z range. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, boolean) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetricLH(double, double, double, double, boolean) orthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetricLH(double, double, double, double, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3d setOrthoSymmetricLH(double width, double height, double zNear, double zFar, boolean zZeroToOne) { + m00 = 2.0 / width; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 2.0 / height; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear); + m30 = 0.0; + m31 = 0.0; + m32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + properties = 0; + return this; + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * This method is equivalent to calling {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetricLH(double, double, double, double) orthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetricLH(double, double, double, double) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3d setOrthoSymmetricLH(double width, double height, double zNear, double zFar) { + return setOrthoSymmetricLH(width, height, zNear, zFar, false); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4x3d) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2D(double, double, double, double) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double, Matrix4x3d) + * @see #setOrtho2D(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d ortho2D(double left, double right, double bottom, double top, Matrix4x3d dest) { + // calculate right matrix elements + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm30 = -(right + left) / (right - left); + double rm31 = -(top + bottom) / (top - bottom); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = -m20; + dest.m21 = -m21; + dest.m22 = -m22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2D(double, double, double, double) setOrtho2D()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double) + * @see #setOrtho2D(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4x3d ortho2D(double left, double right, double bottom, double top) { + return ortho2D(left, right, bottom, top, this); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4x3d) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2DLH(double, double, double, double) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double, Matrix4x3d) + * @see #setOrtho2DLH(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d ortho2DLH(double left, double right, double bottom, double top, Matrix4x3d dest) { + // calculate right matrix elements + double rm00 = 2.0 / (right - left); + double rm11 = 2.0 / (top - bottom); + double rm30 = -(right + left) / (right - left); + double rm31 = -(top + bottom) / (top - bottom); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2DLH(double, double, double, double) setOrtho2DLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double) + * @see #setOrtho2DLH(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4x3d ortho2DLH(double left, double right, double bottom, double top) { + return ortho2DLH(left, right, bottom, top, this); + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with + * zNear=-1 and zFar=+1. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho2D(double, double, double, double) ortho2D()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(double, double, double, double, double, double) + * @see #ortho2D(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4x3d setOrtho2D(double left, double right, double bottom, double top) { + m00 = 2.0 / (right - left); + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 2.0 / (top - bottom); + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = -1.0; + m30 = -(right + left) / (right - left); + m31 = -(top + bottom) / (top - bottom); + m32 = 0.0; + properties = 0; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system. + *

+ * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho2DLH(double, double, double, double) ortho2DLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(double, double, double, double, double, double) + * @see #ortho2DLH(double, double, double, double) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4x3d setOrtho2DLH(double left, double right, double bottom, double top) { + m00 = 2.0 / (right - left); + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 2.0 / (top - bottom); + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + m30 = -(right + left) / (right - left); + m31 = -(top + bottom) / (top - bottom); + m32 = 0.0; + properties = 0; + return this; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3dc, Vector3dc, Vector3dc) lookAt} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3dc, Vector3dc) setLookAlong()}. + * + * @see #lookAlong(double, double, double, double, double, double) + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3d lookAlong(Vector3dc dir, Vector3dc up) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3dc, Vector3dc, Vector3dc) lookAt} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3dc, Vector3dc) setLookAlong()}. + * + * @see #lookAlong(double, double, double, double, double, double) + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d lookAlong(Vector3dc dir, Vector3dc up, Matrix4x3d dest) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()} + * + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * @see #setLookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d lookAlong(double dirX, double dirY, double dirZ, + double upX, double upY, double upZ, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return setLookAlong(dirX, dirY, dirZ, upX, upY, upZ); + + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + double rm00 = leftX; + double rm01 = upnX; + double rm02 = dirX; + double rm10 = leftY; + double rm11 = upnY; + double rm12 = dirY; + double rm20 = leftZ; + double rm21 = upnZ; + double rm22 = dirZ; + + // perform optimized matrix multiplication + // introduce temporaries for dependent results + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set the rest of the matrix elements + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()} + * + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * @see #setLookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d lookAlong(double dirX, double dirY, double dirZ, + double upX, double upY, double upZ) { + return lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * This is equivalent to calling + * {@link #setLookAt(Vector3dc, Vector3dc, Vector3dc) setLookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(Vector3dc, Vector3dc)}. + * + * @see #setLookAlong(Vector3dc, Vector3dc) + * @see #lookAlong(Vector3dc, Vector3dc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3d setLookAlong(Vector3dc dir, Vector3dc up) { + return setLookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * This is equivalent to calling + * {@link #setLookAt(double, double, double, double, double, double, double, double, double) + * setLookAt()} with eye = (0, 0, 0) and center = dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(double, double, double, double, double, double) lookAlong()} + * + * @see #setLookAlong(double, double, double, double, double, double) + * @see #lookAlong(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d setLookAlong(double dirX, double dirY, double dirZ, + double upX, double upY, double upZ) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + m00 = leftX; + m01 = upnX; + m02 = dirX; + m10 = leftY; + m11 = upnY; + m12 = dirY; + m20 = leftZ; + m21 = upnZ; + m22 = dirZ; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + + return this; + } + + /** + * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, that aligns + * -z with center - eye. + *

+ * In order to not make use of vectors to specify eye, center and up but use primitives, + * like in the GLU function, use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()} + * instead. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAt(Vector3dc, Vector3dc, Vector3dc) lookAt()}. + * + * @see #setLookAt(double, double, double, double, double, double, double, double, double) + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3d setLookAt(Vector3dc eye, Vector3dc center, Vector3dc up) { + return setLookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt}. + * + * @see #setLookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d setLookAt(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + m00 = leftX; + m01 = upnX; + m02 = dirX; + m10 = leftY; + m11 = upnY; + m12 = dirY; + m20 = leftZ; + m21 = upnZ; + m22 = dirZ; + m30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + m31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + m32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + properties = PROPERTY_ORTHONORMAL; + + return this; + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d lookAt(Vector3dc eye, Vector3dc center, Vector3dc up, Matrix4x3d dest) { + return lookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #lookAt(double, double, double, double, double, double, double, double, double) + * @see #setLookAlong(Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3d lookAt(Vector3dc eye, Vector3dc center, Vector3dc up) { + return lookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}. + * + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAt(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d lookAt(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + return lookAtGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + } + private Matrix4x3d lookAtGeneric(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4x3d dest) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + double rm00 = leftX; + double rm01 = upnX; + double rm02 = dirX; + double rm10 = leftY; + double rm11 = upnY; + double rm12 = dirY; + double rm20 = leftZ; + double rm21 = upnZ; + double rm22 = dirZ; + double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + + // perform optimized matrix multiplication + // compute last column first, because others do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + // introduce temporaries for dependent results + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set the rest of the matrix elements + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}. + * + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAt(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d lookAt(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ) { + return lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, that aligns + * +z with center - eye. + *

+ * In order to not make use of vectors to specify eye, center and up but use primitives, + * like in the GLU function, use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()} + * instead. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAtLH(Vector3dc, Vector3dc, Vector3dc) lookAt()}. + * + * @see #setLookAtLH(double, double, double, double, double, double, double, double, double) + * @see #lookAtLH(Vector3dc, Vector3dc, Vector3dc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3d setLookAtLH(Vector3dc eye, Vector3dc center, Vector3dc up) { + return setLookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAtLH(double, double, double, double, double, double, double, double, double) lookAtLH}. + * + * @see #setLookAtLH(Vector3dc, Vector3dc, Vector3dc) + * @see #lookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d setLookAtLH(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + m00 = leftX; + m01 = upnX; + m02 = dirX; + m10 = leftY; + m11 = upnY; + m12 = dirY; + m20 = leftZ; + m21 = upnZ; + m22 = dirZ; + m30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + m31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + m32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + properties = PROPERTY_ORTHONORMAL; + + return this; + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #lookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d lookAtLH(Vector3dc eye, Vector3dc center, Vector3dc up, Matrix4x3d dest) { + return lookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #lookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3d lookAtLH(Vector3dc eye, Vector3dc center, Vector3dc up) { + return lookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}. + * + * @see #lookAtLH(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d lookAtLH(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4x3d dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + return lookAtLHGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + } + private Matrix4x3d lookAtLHGeneric(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ, Matrix4x3d dest) { + // Compute direction from position to lookAt + double dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirY * leftZ - dirZ * leftY; + double upnY = dirZ * leftX - dirX * leftZ; + double upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + double rm00 = leftX; + double rm01 = upnX; + double rm02 = dirX; + double rm10 = leftY; + double rm11 = upnY; + double rm12 = dirY; + double rm20 = leftZ; + double rm21 = upnZ; + double rm22 = dirZ; + double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + + // perform optimized matrix multiplication + // compute last column first, because others do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + // introduce temporaries for dependent results + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set the rest of the matrix elements + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}. + * + * @see #lookAtLH(Vector3dc, Vector3dc, Vector3dc) + * @see #setLookAtLH(double, double, double, double, double, double, double, double, double) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d lookAtLH(double eyeX, double eyeY, double eyeZ, + double centerX, double centerY, double centerZ, + double upX, double upY, double upZ) { + return lookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this); + } + + public Vector4d frustumPlane(int which, Vector4d dest) { + switch (which) { + case PLANE_NX: + dest.set(m00, m10, m20, 1.0 + m30).normalize(); + break; + case PLANE_PX: + dest.set(-m00, -m10, -m20, 1.0 - m30).normalize(); + break; + case PLANE_NY: + dest.set(m01, m11, m21, 1.0 + m31).normalize(); + break; + case PLANE_PY: + dest.set(-m01, -m11, -m21, 1.0 - m31).normalize(); + break; + case PLANE_NZ: + dest.set(m02, m12, m22, 1.0 + m32).normalize(); + break; + case PLANE_PZ: + dest.set(-m02, -m12, -m22, 1.0 - m32).normalize(); + break; + default: + throw new IllegalArgumentException("which"); //$NON-NLS-1$ + } + return dest; + } + + public Vector3d positiveZ(Vector3d dir) { + dir.x = m10 * m21 - m11 * m20; + dir.y = m20 * m01 - m21 * m00; + dir.z = m00 * m11 - m01 * m10; + return dir.normalize(dir); + } + + public Vector3d normalizedPositiveZ(Vector3d dir) { + dir.x = m02; + dir.y = m12; + dir.z = m22; + return dir; + } + + public Vector3d positiveX(Vector3d dir) { + dir.x = m11 * m22 - m12 * m21; + dir.y = m02 * m21 - m01 * m22; + dir.z = m01 * m12 - m02 * m11; + return dir.normalize(dir); + } + + public Vector3d normalizedPositiveX(Vector3d dir) { + dir.x = m00; + dir.y = m10; + dir.z = m20; + return dir; + } + + public Vector3d positiveY(Vector3d dir) { + dir.x = m12 * m20 - m10 * m22; + dir.y = m00 * m22 - m02 * m20; + dir.z = m02 * m10 - m00 * m12; + return dir.normalize(dir); + } + + public Vector3d normalizedPositiveY(Vector3d dir) { + dir.x = m01; + dir.y = m11; + dir.z = m21; + return dir; + } + + public Vector3d origin(Vector3d origin) { + double a = m00 * m11 - m01 * m10; + double b = m00 * m12 - m02 * m10; + double d = m01 * m12 - m02 * m11; + double g = m20 * m31 - m21 * m30; + double h = m20 * m32 - m22 * m30; + double j = m21 * m32 - m22 * m31; + origin.x = -m10 * j + m11 * h - m12 * g; + origin.y = m00 * j - m01 * h + m02 * g; + origin.z = -m30 * d + m31 * b - m32 * a; + return origin; + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction light. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param light + * the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4x3d shadow(Vector4dc light, double a, double b, double c, double d) { + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, this); + } + + public Matrix4x3d shadow(Vector4dc light, double a, double b, double c, double d, Matrix4x3d dest) { + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW). + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param lightX + * the x-component of the light's vector + * @param lightY + * the y-component of the light's vector + * @param lightZ + * the z-component of the light's vector + * @param lightW + * the w-component of the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4x3d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d) { + return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, this); + } + + public Matrix4x3d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d, Matrix4x3d dest) { + // normalize plane + double invPlaneLen = Math.invsqrt(a*a + b*b + c*c); + double an = a * invPlaneLen; + double bn = b * invPlaneLen; + double cn = c * invPlaneLen; + double dn = d * invPlaneLen; + + double dot = an * lightX + bn * lightY + cn * lightZ + dn * lightW; + + // compute right matrix elements + double rm00 = dot - an * lightX; + double rm01 = -an * lightY; + double rm02 = -an * lightZ; + double rm03 = -an * lightW; + double rm10 = -bn * lightX; + double rm11 = dot - bn * lightY; + double rm12 = -bn * lightZ; + double rm13 = -bn * lightW; + double rm20 = -cn * lightX; + double rm21 = -cn * lightY; + double rm22 = dot - cn * lightZ; + double rm23 = -cn * lightW; + double rm30 = -dn * lightX; + double rm31 = -dn * lightY; + double rm32 = -dn * lightZ; + double rm33 = dot - dn * lightW; + + // matrix multiplication + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02 + m30 * rm03; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02 + m31 * rm03; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02 + m32 * rm03; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12 + m30 * rm13; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12 + m31 * rm13; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12 + m32 * rm13; + double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30 * rm23; + double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31 * rm23; + double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32 * rm23; + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30 * rm33; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31 * rm33; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32 * rm33; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + public Matrix4x3d shadow(Vector4dc light, Matrix4x3dc planeTransform, Matrix4x3d dest) { + // compute plane equation by transforming (y = 0) + double a = planeTransform.m10(); + double b = planeTransform.m11(); + double c = planeTransform.m12(); + double d = -a * planeTransform.m30() - b * planeTransform.m31() - c * planeTransform.m32(); + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction light. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param light + * the light's vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @return this + */ + public Matrix4x3d shadow(Vector4dc light, Matrix4x3dc planeTransform) { + return shadow(light, planeTransform, this); + } + + public Matrix4x3d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4x3dc planeTransform, Matrix4x3d dest) { + // compute plane equation by transforming (y = 0) + double a = planeTransform.m10(); + double b = planeTransform.m11(); + double c = planeTransform.m12(); + double d = -a * planeTransform.m30() - b * planeTransform.m31() - c * planeTransform.m32(); + return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW). + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param lightX + * the x-component of the light vector + * @param lightY + * the y-component of the light vector + * @param lightZ + * the z-component of the light vector + * @param lightW + * the w-component of the light vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @return this + */ + public Matrix4x3d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4x3dc planeTransform) { + return shadow(lightX, lightY, lightZ, lightW, planeTransform, this); + } + + /** + * Set this matrix to a cylindrical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos while constraining a cylindrical rotation around the given up vector. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @param up + * the rotation axis (must be {@link Vector3d#normalize() normalized}) + * @return this + */ + public Matrix4x3d billboardCylindrical(Vector3dc objPos, Vector3dc targetPos, Vector3dc up) { + double dirX = targetPos.x() - objPos.x(); + double dirY = targetPos.y() - objPos.y(); + double dirZ = targetPos.z() - objPos.z(); + // left = up x dir + double leftX = up.y() * dirZ - up.z() * dirY; + double leftY = up.z() * dirX - up.x() * dirZ; + double leftZ = up.x() * dirY - up.y() * dirX; + // normalize left + double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLen; + leftY *= invLeftLen; + leftZ *= invLeftLen; + // recompute dir by constraining rotation around 'up' + // dir = left x up + dirX = leftY * up.z() - leftZ * up.y(); + dirY = leftZ * up.x() - leftX * up.z(); + dirZ = leftX * up.y() - leftY * up.x(); + // normalize dir + double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLen; + dirY *= invDirLen; + dirZ *= invDirLen; + // set matrix elements + m00 = leftX; + m01 = leftY; + m02 = leftZ; + m10 = up.x(); + m11 = up.y(); + m12 = up.z(); + m20 = dirX; + m21 = dirY; + m22 = dirZ; + m30 = objPos.x(); + m31 = objPos.y(); + m32 = objPos.z(); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + *

+ * If preserving an up vector is not necessary when rotating the +Z axis, then a shortest arc rotation can be obtained + * using {@link #billboardSpherical(Vector3dc, Vector3dc)}. + * + * @see #billboardSpherical(Vector3dc, Vector3dc) + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @param up + * the up axis used to orient the object + * @return this + */ + public Matrix4x3d billboardSpherical(Vector3dc objPos, Vector3dc targetPos, Vector3dc up) { + double dirX = targetPos.x() - objPos.x(); + double dirY = targetPos.y() - objPos.y(); + double dirZ = targetPos.z() - objPos.z(); + // normalize dir + double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLen; + dirY *= invDirLen; + dirZ *= invDirLen; + // left = up x dir + double leftX = up.y() * dirZ - up.z() * dirY; + double leftY = up.z() * dirX - up.x() * dirZ; + double leftZ = up.x() * dirY - up.y() * dirX; + // normalize left + double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLen; + leftY *= invLeftLen; + leftZ *= invLeftLen; + // up = dir x left + double upX = dirY * leftZ - dirZ * leftY; + double upY = dirZ * leftX - dirX * leftZ; + double upZ = dirX * leftY - dirY * leftX; + // set matrix elements + m00 = leftX; + m01 = leftY; + m02 = leftZ; + m10 = upX; + m11 = upY; + m12 = upZ; + m20 = dirX; + m21 = dirY; + m22 = dirZ; + m30 = objPos.x(); + m31 = objPos.y(); + m32 = objPos.z(); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos using a shortest arc rotation by not preserving any up vector of the object. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + *

+ * In order to specify an up vector which needs to be maintained when rotating the +Z axis of the object, + * use {@link #billboardSpherical(Vector3dc, Vector3dc, Vector3dc)}. + * + * @see #billboardSpherical(Vector3dc, Vector3dc, Vector3dc) + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @return this + */ + public Matrix4x3d billboardSpherical(Vector3dc objPos, Vector3dc targetPos) { + double toDirX = targetPos.x() - objPos.x(); + double toDirY = targetPos.y() - objPos.y(); + double toDirZ = targetPos.z() - objPos.z(); + double x = -toDirY; + double y = toDirX; + double w = Math.sqrt(toDirX * toDirX + toDirY * toDirY + toDirZ * toDirZ) + toDirZ; + double invNorm = Math.invsqrt(x * x + y * y + w * w); + x *= invNorm; + y *= invNorm; + w *= invNorm; + double q00 = (x + x) * x; + double q11 = (y + y) * y; + double q01 = (x + x) * y; + double q03 = (x + x) * w; + double q13 = (y + y) * w; + m00 = 1.0 - q11; + m01 = q01; + m02 = -q13; + m10 = q01; + m11 = 1.0 - q00; + m12 = q03; + m20 = q13; + m21 = -q03; + m22 = 1.0 - q11 - q00; + m30 = objPos.x(); + m31 = objPos.y(); + m32 = objPos.z(); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(m00); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m01); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m02); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m10); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m11); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m12); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m20); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m21); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m22); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m30); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m31); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m32); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof Matrix4x3d)) + return false; + Matrix4x3d other = (Matrix4x3d) obj; + if (Double.doubleToLongBits(m00) != Double.doubleToLongBits(other.m00)) + return false; + if (Double.doubleToLongBits(m01) != Double.doubleToLongBits(other.m01)) + return false; + if (Double.doubleToLongBits(m02) != Double.doubleToLongBits(other.m02)) + return false; + if (Double.doubleToLongBits(m10) != Double.doubleToLongBits(other.m10)) + return false; + if (Double.doubleToLongBits(m11) != Double.doubleToLongBits(other.m11)) + return false; + if (Double.doubleToLongBits(m12) != Double.doubleToLongBits(other.m12)) + return false; + if (Double.doubleToLongBits(m20) != Double.doubleToLongBits(other.m20)) + return false; + if (Double.doubleToLongBits(m21) != Double.doubleToLongBits(other.m21)) + return false; + if (Double.doubleToLongBits(m22) != Double.doubleToLongBits(other.m22)) + return false; + if (Double.doubleToLongBits(m30) != Double.doubleToLongBits(other.m30)) + return false; + if (Double.doubleToLongBits(m31) != Double.doubleToLongBits(other.m31)) + return false; + if (Double.doubleToLongBits(m32) != Double.doubleToLongBits(other.m32)) + return false; + return true; + } + + public boolean equals(Matrix4x3dc m, double delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix4x3d)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m02, m.m02(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + if (!Runtime.equals(m12, m.m12(), delta)) + return false; + if (!Runtime.equals(m20, m.m20(), delta)) + return false; + if (!Runtime.equals(m21, m.m21(), delta)) + return false; + if (!Runtime.equals(m22, m.m22(), delta)) + return false; + if (!Runtime.equals(m30, m.m30(), delta)) + return false; + if (!Runtime.equals(m31, m.m31(), delta)) + return false; + if (!Runtime.equals(m32, m.m32(), delta)) + return false; + return true; + } + + public Matrix4x3d pick(double x, double y, double width, double height, int[] viewport, Matrix4x3d dest) { + double sx = viewport[2] / width; + double sy = viewport[3] / height; + double tx = (viewport[2] + 2.0 * (viewport[0] - x)) / width; + double ty = (viewport[3] + 2.0 * (viewport[1] - y)) / height; + dest.m30 = m00 * tx + m10 * ty + m30; + dest.m31 = m01 * tx + m11 * ty + m31; + dest.m32 = m02 * tx + m12 * ty + m32; + dest.m00 = m00 * sx; + dest.m01 = m01 * sx; + dest.m02 = m02 * sx; + dest.m10 = m10 * sy; + dest.m11 = m11 * sy; + dest.m12 = m12 * sy; + dest.properties = 0; + return dest; + } + + /** + * Apply a picking transformation to this matrix using the given window coordinates (x, y) as the pick center + * and the given (width, height) as the size of the picking region in window coordinates. + * + * @param x + * the x coordinate of the picking region center in window coordinates + * @param y + * the y coordinate of the picking region center in window coordinates + * @param width + * the width of the picking region in window coordinates + * @param height + * the height of the picking region in window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @return this + */ + public Matrix4x3d pick(double x, double y, double width, double height, int[] viewport) { + return pick(x, y, width, height, viewport, this); + } + + /** + * Exchange the values of this matrix with the given other matrix. + * + * @param other + * the other matrix to exchange the values with + * @return this + */ + public Matrix4x3d swap(Matrix4x3d other) { + double tmp; + tmp = m00; m00 = other.m00; other.m00 = tmp; + tmp = m01; m01 = other.m01; other.m01 = tmp; + tmp = m02; m02 = other.m02; other.m02 = tmp; + tmp = m10; m10 = other.m10; other.m10 = tmp; + tmp = m11; m11 = other.m11; other.m11 = tmp; + tmp = m12; m12 = other.m12; other.m12 = tmp; + tmp = m20; m20 = other.m20; other.m20 = tmp; + tmp = m21; m21 = other.m21; other.m21 = tmp; + tmp = m22; m22 = other.m22; other.m22 = tmp; + tmp = m30; m30 = other.m30; other.m30 = tmp; + tmp = m31; m31 = other.m31; other.m31 = tmp; + tmp = m32; m32 = other.m32; other.m32 = tmp; + int props = properties; + this.properties = other.properties; + other.properties = props; + return this; + } + + public Matrix4x3d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY, Matrix4x3d dest) { + double m30 = m20 * -radius + this.m30; + double m31 = m21 * -radius + this.m31; + double m32 = m22 * -radius + this.m32; + double sin = Math.sin(angleX); + double cos = Math.cosFromSin(sin, angleX); + double nm10 = m10 * cos + m20 * sin; + double nm11 = m11 * cos + m21 * sin; + double nm12 = m12 * cos + m22 * sin; + double m20 = this.m20 * cos - m10 * sin; + double m21 = this.m21 * cos - m11 * sin; + double m22 = this.m22 * cos - m12 * sin; + sin = Math.sin(angleY); + cos = Math.cosFromSin(sin, angleY); + double nm00 = m00 * cos - m20 * sin; + double nm01 = m01 * cos - m21 * sin; + double nm02 = m02 * cos - m22 * sin; + double nm20 = m00 * sin + m20 * cos; + double nm21 = m01 * sin + m21 * cos; + double nm22 = m02 * sin + m22 * cos; + dest.m30 = -nm00 * centerX - nm10 * centerY - nm20 * centerZ + m30; + dest.m31 = -nm01 * centerX - nm11 * centerY - nm21 * centerZ + m31; + dest.m32 = -nm02 * centerX - nm12 * centerY - nm22 * centerZ + m32; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + public Matrix4x3d arcball(double radius, Vector3dc center, double angleX, double angleY, Matrix4x3d dest) { + return arcball(radius, center.x(), center.y(), center.z(), angleX, angleY, dest); + } + + /** + * Apply an arcball view transformation to this matrix with the given radius and center (centerX, centerY, centerZ) + * position of the arcball and the specified X and Y rotation angles. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ) + * + * @param radius + * the arcball radius + * @param centerX + * the x coordinate of the center position of the arcball + * @param centerY + * the y coordinate of the center position of the arcball + * @param centerZ + * the z coordinate of the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @return this + */ + public Matrix4x3d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY) { + return arcball(radius, centerX, centerY, centerZ, angleX, angleY, this); + } + + /** + * Apply an arcball view transformation to this matrix with the given radius and center + * position of the arcball and the specified X and Y rotation angles. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z) + * + * @param radius + * the arcball radius + * @param center + * the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @return this + */ + public Matrix4x3d arcball(double radius, Vector3dc center, double angleX, double angleY) { + return arcball(radius, center.x(), center.y(), center.z(), angleX, angleY, this); + } + + public Matrix4x3d transformAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vector3d outMin, Vector3d outMax) { + double xax = m00 * minX, xay = m01 * minX, xaz = m02 * minX; + double xbx = m00 * maxX, xby = m01 * maxX, xbz = m02 * maxX; + double yax = m10 * minY, yay = m11 * minY, yaz = m12 * minY; + double ybx = m10 * maxY, yby = m11 * maxY, ybz = m12 * maxY; + double zax = m20 * minZ, zay = m21 * minZ, zaz = m22 * minZ; + double zbx = m20 * maxZ, zby = m21 * maxZ, zbz = m22 * maxZ; + double xminx, xminy, xminz, yminx, yminy, yminz, zminx, zminy, zminz; + double xmaxx, xmaxy, xmaxz, ymaxx, ymaxy, ymaxz, zmaxx, zmaxy, zmaxz; + if (xax < xbx) { + xminx = xax; + xmaxx = xbx; + } else { + xminx = xbx; + xmaxx = xax; + } + if (xay < xby) { + xminy = xay; + xmaxy = xby; + } else { + xminy = xby; + xmaxy = xay; + } + if (xaz < xbz) { + xminz = xaz; + xmaxz = xbz; + } else { + xminz = xbz; + xmaxz = xaz; + } + if (yax < ybx) { + yminx = yax; + ymaxx = ybx; + } else { + yminx = ybx; + ymaxx = yax; + } + if (yay < yby) { + yminy = yay; + ymaxy = yby; + } else { + yminy = yby; + ymaxy = yay; + } + if (yaz < ybz) { + yminz = yaz; + ymaxz = ybz; + } else { + yminz = ybz; + ymaxz = yaz; + } + if (zax < zbx) { + zminx = zax; + zmaxx = zbx; + } else { + zminx = zbx; + zmaxx = zax; + } + if (zay < zby) { + zminy = zay; + zmaxy = zby; + } else { + zminy = zby; + zmaxy = zay; + } + if (zaz < zbz) { + zminz = zaz; + zmaxz = zbz; + } else { + zminz = zbz; + zmaxz = zaz; + } + outMin.x = xminx + yminx + zminx + m30; + outMin.y = xminy + yminy + zminy + m31; + outMin.z = xminz + yminz + zminz + m32; + outMax.x = xmaxx + ymaxx + zmaxx + m30; + outMax.y = xmaxy + ymaxy + zmaxy + m31; + outMax.z = xmaxz + ymaxz + zmaxz + m32; + return this; + } + + public Matrix4x3d transformAab(Vector3dc min, Vector3dc max, Vector3d outMin, Vector3d outMax) { + return transformAab(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), outMin, outMax); + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Matrix4x3d lerp(Matrix4x3dc other, double t) { + return lerp(other, t, this); + } + + public Matrix4x3d lerp(Matrix4x3dc other, double t, Matrix4x3d dest) { + dest.m00 = Math.fma(other.m00() - m00, t, m00); + dest.m01 = Math.fma(other.m01() - m01, t, m01); + dest.m02 = Math.fma(other.m02() - m02, t, m02); + dest.m10 = Math.fma(other.m10() - m10, t, m10); + dest.m11 = Math.fma(other.m11() - m11, t, m11); + dest.m12 = Math.fma(other.m12() - m12, t, m12); + dest.m20 = Math.fma(other.m20() - m20, t, m20); + dest.m21 = Math.fma(other.m21() - m21, t, m21); + dest.m22 = Math.fma(other.m22() - m22, t, m22); + dest.m30 = Math.fma(other.m30() - m30, t, m30); + dest.m31 = Math.fma(other.m31() - m31, t, m31); + dest.m32 = Math.fma(other.m32() - m32, t, m32); + dest.properties = properties & other.properties(); + return dest; + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3dc, Vector3dc) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix4x3d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invert(), dest) + * + * @see #rotateTowards(double, double, double, double, double, double, Matrix4x3d) + * @see #rotationTowards(Vector3dc, Vector3dc) + * + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateTowards(Vector3dc dir, Vector3dc up, Matrix4x3d dest) { + return rotateTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3dc, Vector3dc) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix4x3d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invert()) + * + * @see #rotateTowards(double, double, double, double, double, double) + * @see #rotationTowards(Vector3dc, Vector3dc) + * + * @param dir + * the direction to orient towards + * @param up + * the up vector + * @return this + */ + public Matrix4x3d rotateTowards(Vector3dc dir, Vector3dc up) { + return rotateTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ). + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix4x3d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invert()) + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + return rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ) + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix4x3d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invert(), dest) + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix4x3d dest) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double ndirX = dirX * invDirLength; + double ndirY = dirY * invDirLength; + double ndirZ = dirZ * invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = ndirY * leftZ - ndirZ * leftY; + double upnY = ndirZ * leftX - ndirX * leftZ; + double upnZ = ndirX * leftY - ndirY * leftX; + double rm00 = leftX; + double rm01 = leftY; + double rm02 = leftZ; + double rm10 = upnX; + double rm11 = upnY; + double rm12 = upnZ; + double rm20 = ndirX; + double rm21 = ndirY; + double rm22 = ndirZ; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with dir. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAt(new Vector3d(), new Vector3d(dir).negate(), up).invert() + * + * @see #rotationTowards(Vector3dc, Vector3dc) + * @see #rotateTowards(double, double, double, double, double, double) + * + * @param dir + * the direction to orient the local -z axis towards + * @param up + * the up vector + * @return this + */ + public Matrix4x3d rotationTowards(Vector3dc dir, Vector3dc up) { + return rotationTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with (dirX, dirY, dirZ). + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invert() + * + * @see #rotateTowards(Vector3dc, Vector3dc) + * @see #rotationTowards(double, double, double, double, double, double) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d rotationTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double ndirX = dirX * invDirLength; + double ndirY = dirY * invDirLength; + double ndirZ = dirZ * invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = ndirY * leftZ - ndirZ * leftY; + double upnY = ndirZ * leftX - ndirX * leftZ; + double upnZ = ndirX * leftY - ndirY * leftX; + this.m00 = leftX; + this.m01 = leftY; + this.m02 = leftZ; + this.m10 = upnX; + this.m11 = upnY; + this.m12 = upnZ; + this.m20 = ndirX; + this.m21 = ndirY; + this.m22 = ndirZ; + this.m30 = 0.0; + this.m31 = 0.0; + this.m32 = 0.0; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that translates to the given pos and aligns the local -z + * axis with dir. + *

+ * This method is equivalent to calling: translation(pos).rotateTowards(dir, up) + * + * @see #translation(Vector3dc) + * @see #rotateTowards(Vector3dc, Vector3dc) + * + * @param pos + * the position to translate to + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @return this + */ + public Matrix4x3d translationRotateTowards(Vector3dc pos, Vector3dc dir, Vector3dc up) { + return translationRotateTowards(pos.x(), pos.y(), pos.z(), dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that translates to the given (posX, posY, posZ) and aligns the local -z + * axis with (dirX, dirY, dirZ). + *

+ * This method is equivalent to calling: translation(posX, posY, posZ).rotateTowards(dirX, dirY, dirZ, upX, upY, upZ) + * + * @see #translation(double, double, double) + * @see #rotateTowards(double, double, double, double, double, double) + * + * @param posX + * the x-coordinate of the position to translate to + * @param posY + * the y-coordinate of the position to translate to + * @param posZ + * the z-coordinate of the position to translate to + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3d translationRotateTowards(double posX, double posY, double posZ, double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double ndirX = dirX * invDirLength; + double ndirY = dirY * invDirLength; + double ndirZ = dirZ * invDirLength; + // left = up x direction + double leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = ndirY * leftZ - ndirZ * leftY; + double upnY = ndirZ * leftX - ndirX * leftZ; + double upnZ = ndirX * leftY - ndirY * leftX; + this.m00 = leftX; + this.m01 = leftY; + this.m02 = leftZ; + this.m10 = upnX; + this.m11 = upnY; + this.m12 = upnZ; + this.m20 = ndirX; + this.m21 = ndirY; + this.m22 = ndirZ; + this.m30 = posX; + this.m31 = posY; + this.m32 = posZ; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + public Vector3d getEulerAnglesZYX(Vector3d dest) { + dest.x = Math.atan2(m12, m22); + dest.y = Math.atan2(-m02, Math.sqrt(1.0 - m02 * m02)); + dest.z = Math.atan2(m01, m00); + return dest; + } + + public Vector3d getEulerAnglesXYZ(Vector3d dest) { + dest.x = Math.atan2(-m21, m22); + dest.y = Math.atan2(m20, Math.sqrt(1.0 - m20 * m20)); + dest.z = Math.atan2(-m10, m00); + return dest; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @return this + */ + public Matrix4x3d obliqueZ(double a, double b) { + this.m20 = m00 * a + m10 * b + m20; + this.m21 = m01 * a + m11 * b + m21; + this.m22 = m02 * a + m12 * b + m22; + this.properties = 0; + return this; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3d obliqueZ(double a, double b, Matrix4x3d dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m00 * a + m10 * b + m20; + dest.m21 = m01 * a + m11 * b + m21; + dest.m22 = m02 * a + m12 * b + m22; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = 0; + return dest; + } + + /** + * Multiply this by the matrix + *
+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapXZY() { + return mapXZY(this); + } + public Matrix4x3d mapXZY(Matrix4x3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m20)._m11(m21)._m12(m22)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapXZnY() { + return mapXZnY(this); + } + public Matrix4x3d mapXZnY(Matrix4x3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m20)._m11(m21)._m12(m22)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapXnYnZ() { + return mapXnYnZ(this); + } + public Matrix4x3d mapXnYnZ(Matrix4x3d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapXnZY() { + return mapXnZY(this); + } + public Matrix4x3d mapXnZY(Matrix4x3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapXnZnY() { + return mapXnZnY(this); + } + public Matrix4x3d mapXnZnY(Matrix4x3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapYXZ() { + return mapYXZ(this); + } + public Matrix4x3d mapYXZ(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m00)._m11(m01)._m12(m02)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapYXnZ() { + return mapYXnZ(this); + } + public Matrix4x3d mapYXnZ(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m00)._m11(m01)._m12(m02)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapYZX() { + return mapYZX(this); + } + public Matrix4x3d mapYZX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m20)._m11(m21)._m12(m22)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapYZnX() { + return mapYZnX(this); + } + public Matrix4x3d mapYZnX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m20)._m11(m21)._m12(m22)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapYnXZ() { + return mapYnXZ(this); + } + public Matrix4x3d mapYnXZ(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapYnXnZ() { + return mapYnXnZ(this); + } + public Matrix4x3d mapYnXnZ(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapYnZX() { + return mapYnZX(this); + } + public Matrix4x3d mapYnZX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapYnZnX() { + return mapYnZnX(this); + } + public Matrix4x3d mapYnZnX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 0 0 1 0
+     * 1 0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapZXY() { + return mapZXY(this); + } + public Matrix4x3d mapZXY(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m00)._m11(m01)._m12(m02)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapZXnY() { + return mapZXnY(this); + } + public Matrix4x3d mapZXnY(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m00)._m11(m01)._m12(m02)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 0 1 0 0
+     * 1 0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapZYX() { + return mapZYX(this); + } + public Matrix4x3d mapZYX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m10)._m11(m11)._m12(m12)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 1 0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapZYnX() { + return mapZYnX(this); + } + public Matrix4x3d mapZYnX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m10)._m11(m11)._m12(m12)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 1  0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapZnXY() { + return mapZnXY(this); + } + public Matrix4x3d mapZnXY(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapZnXnY() { + return mapZnXnY(this); + } + public Matrix4x3d mapZnXnY(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapZnYX() { + return mapZnYX(this); + } + public Matrix4x3d mapZnYX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapZnYnX() { + return mapZnYnX(this); + } + public Matrix4x3d mapZnYnX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnXYnZ() { + return mapnXYnZ(this); + } + public Matrix4x3d mapnXYnZ(Matrix4x3d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m10)._m11(m11)._m12(m12)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnXZY() { + return mapnXZY(this); + } + public Matrix4x3d mapnXZY(Matrix4x3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m20)._m11(m21)._m12(m22)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnXZnY() { + return mapnXZnY(this); + } + public Matrix4x3d mapnXZnY(Matrix4x3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m20)._m11(m21)._m12(m22)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnXnYZ() { + return mapnXnYZ(this); + } + public Matrix4x3d mapnXnYZ(Matrix4x3d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnXnYnZ() { + return mapnXnYnZ(this); + } + public Matrix4x3d mapnXnYnZ(Matrix4x3d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0 -1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnXnZY() { + return mapnXnZY(this); + } + public Matrix4x3d mapnXnZY(Matrix4x3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnXnZnY() { + return mapnXnZnY(this); + } + public Matrix4x3d mapnXnZnY(Matrix4x3d dest) { + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnYXZ() { + return mapnYXZ(this); + } + public Matrix4x3d mapnYXZ(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m00)._m11(m01)._m12(m02)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnYXnZ() { + return mapnYXnZ(this); + } + public Matrix4x3d mapnYXnZ(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m00)._m11(m01)._m12(m02)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnYZX() { + return mapnYZX(this); + } + public Matrix4x3d mapnYZX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m20)._m11(m21)._m12(m22)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnYZnX() { + return mapnYZnX(this); + } + public Matrix4x3d mapnYZnX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m20)._m11(m21)._m12(m22)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnYnXZ() { + return mapnYnXZ(this); + } + public Matrix4x3d mapnYnXZ(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnYnXnZ() { + return mapnYnXnZ(this); + } + public Matrix4x3d mapnYnXnZ(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0 -1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnYnZX() { + return mapnYnZX(this); + } + public Matrix4x3d mapnYnZX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0 -1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnYnZnX() { + return mapnYnZnX(this); + } + public Matrix4x3d mapnYnZnX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     *  0 0 1 0
+     * -1 0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnZXY() { + return mapnZXY(this); + } + public Matrix4x3d mapnZXY(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m00)._m11(m01)._m12(m02)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     *  0 0 -1 0
+     * -1 0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnZXnY() { + return mapnZXnY(this); + } + public Matrix4x3d mapnZXnY(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m00)._m11(m01)._m12(m02)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     *  0 1 0 0
+     * -1 0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnZYX() { + return mapnZYX(this); + } + public Matrix4x3d mapnZYX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m10)._m11(m11)._m12(m12)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     *  0 1  0 0
+     * -1 0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnZYnX() { + return mapnZYnX(this); + } + public Matrix4x3d mapnZYnX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m10)._m11(m11)._m12(m12)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     *  0  0 1 0
+     * -1  0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnZnXY() { + return mapnZnXY(this); + } + public Matrix4x3d mapnZnXY(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * -1  0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnZnXnY() { + return mapnZnXnY(this); + } + public Matrix4x3d mapnZnXnY(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + double m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     *  0 -1 0 0
+     * -1  0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnZnYX() { + return mapnZnYX(this); + } + public Matrix4x3d mapnZnYX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * -1  0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3d mapnZnYnX() { + return mapnZnYnX(this); + } + public Matrix4x3d mapnZnYnX(Matrix4x3d dest) { + double m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d negateX() { + return _m00(-m00)._m01(-m01)._m02(-m02)._properties(properties & PROPERTY_ORTHONORMAL); + } + public Matrix4x3d negateX(Matrix4x3d dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m10)._m11(m11)._m12(m12)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d negateY() { + return _m10(-m10)._m11(-m11)._m12(-m12)._properties(properties & PROPERTY_ORTHONORMAL); + } + public Matrix4x3d negateY(Matrix4x3d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3d negateZ() { + return _m20(-m20)._m21(-m21)._m22(-m22)._properties(properties & PROPERTY_ORTHONORMAL); + } + public Matrix4x3d negateZ(Matrix4x3d dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m10)._m11(m11)._m12(m12)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && + Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && + Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22) && + Math.isFinite(m30) && Math.isFinite(m31) && Math.isFinite(m32); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3dStack.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3dStack.java new file mode 100644 index 000000000..3e2ef7b22 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3dStack.java @@ -0,0 +1,186 @@ +/* + * The MIT License + * + * Copyright (c) 2018-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A stack of many {@link Matrix4x3d} instances. This resembles the matrix stack known from legacy OpenGL. + *

+ * This {@link Matrix4x3dStack} class inherits from {@link Matrix4x3d}, so the current/top matrix is always the + * {@link Matrix4x3dStack}/{@link Matrix4x3d} itself. This affects all operations in {@link Matrix4x3d} that take + * another {@link Matrix4x3d} as parameter. If a {@link Matrix4x3dStack} is used as argument to those methods, the + * effective argument will always be the current matrix of the matrix stack. + * + * @author Kai Burjack + */ +public class Matrix4x3dStack extends Matrix4x3d { + + private static final long serialVersionUID = 1L; + + /** + * The matrix stack as a non-growable array. The size of the stack must be specified in the {@link #Matrix4x3dStack(int) constructor}. + */ + private Matrix4x3d[] mats; + + /** + * The index of the "current" matrix within {@link #mats}. + */ + private int curr; + + /** + * Create a new {@link Matrix4x3dStack} of the given size. + *

+ * Initially the stack pointer is at zero and the current matrix is set to identity. + * + * @param stackSize + * the size of the stack. This must be at least 1, in which case the {@link Matrix4x3dStack} simply only consists of this + * {@link Matrix4x3d} + */ + public Matrix4x3dStack(int stackSize) { + if (stackSize < 1) { + throw new IllegalArgumentException("stackSize must be >= 1"); //$NON-NLS-1$ + } + mats = new Matrix4x3d[stackSize - 1]; + // Allocate all matrices up front to keep the promise of being "allocation-free" + for (int i = 0; i < mats.length; i++) { + mats[i] = new Matrix4x3d(); + } + } + + /** + * Do not invoke manually! Only meant for serialization. + *

+ * Invoking this constructor from client code will result in an inconsistent state of the + * created {@link Matrix4x3dStack} instance. + */ + public Matrix4x3dStack() { + /* Empty! */ + } + + /** + * Set the stack pointer to zero and set the current/bottom matrix to {@link #identity() identity}. + * + * @return this + */ + public Matrix4x3dStack clear() { + curr = 0; + identity(); + return this; + } + + /** + * Increment the stack pointer by one and set the values of the new current matrix to the one directly below it. + * + * @return this + */ + public Matrix4x3dStack pushMatrix() { + if (curr == mats.length) { + throw new IllegalStateException("max stack size of " + (curr + 1) + " reached"); //$NON-NLS-1$ //$NON-NLS-2$ + } + mats[curr++].set(this); + return this; + } + + /** + * Decrement the stack pointer by one. + *

+ * This will effectively dispose of the current matrix. + * + * @return this + */ + public Matrix4x3dStack popMatrix() { + if (curr == 0) { + throw new IllegalStateException("already at the bottom of the stack"); //$NON-NLS-1$ + } + set(mats[--curr]); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + curr; + for (int i = 0; i < curr; i++) { + result = prime * result + mats[i].hashCode(); + } + return result; + } + + /* + * Contract between Matrix4x3d and Matrix4x3dStack: + * + * - Matrix4x3d.equals(Matrix4x3dStack) is true iff all the 12 matrix elements are equal + * - Matrix4x3dStack.equals(Matrix4x3d) is true iff all the 12 matrix elements are equal + * - Matrix4x3dStack.equals(Matrix4x3dStack) is true iff all 12 matrix elements are equal AND the matrix arrays as well as the stack pointer are equal + * - everything else is inequal + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (obj instanceof Matrix4x3dStack) { + Matrix4x3dStack other = (Matrix4x3dStack) obj; + if (curr != other.curr) + return false; + for (int i = 0; i < curr; i++) { + if (!mats[i].equals(other.mats[i])) + return false; + } + } + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(curr); + for (int i = 0; i < curr; i++) { + out.writeObject(mats[i]); + } + } + + public void readExternal(ObjectInput in) throws IOException { + super.readExternal(in); + curr = in.readInt(); + mats = new Matrix4x3dStack[curr]; + for (int i = 0; i < curr; i++) { + Matrix4x3d m = new Matrix4x3d(); + m.readExternal(in); + mats[i] = m; + } + } + + public Object clone() throws CloneNotSupportedException { + Matrix4x3dStack cloned = (Matrix4x3dStack) super.clone(); + Matrix4x3d[] clonedMats = new Matrix4x3d[mats.length]; + for (int i = 0; i < mats.length; i++) + clonedMats[i] = (Matrix4x3d) mats[i].clone(); + cloned.mats = clonedMats; + return cloned; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3dc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3dc.java new file mode 100644 index 000000000..b1f4da4bf --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3dc.java @@ -0,0 +1,3821 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 4x3 matrix of double-precision floats. + * + * @author Kai Burjack + */ +public interface Matrix4x3dc { + + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation x=-1 when using the identity matrix. + */ + int PLANE_NX = 0; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation x=1 when using the identity matrix. + */ + int PLANE_PX = 1; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation y=-1 when using the identity matrix. + */ + int PLANE_NY = 2; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation y=1 when using the identity matrix. + */ + int PLANE_PY = 3; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation z=-1 when using the identity matrix. + */ + int PLANE_NZ = 4; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)} + * identifying the plane with equation z=1 when using the identity matrix. + */ + int PLANE_PZ = 5; + + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents the identity transformation. + */ + byte PROPERTY_IDENTITY = 1<<2; + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents a pure translation transformation. + */ + byte PROPERTY_TRANSLATION = 1<<3; + /** + * Bit returned by {@link #properties()} to indicate that the left 3x3 submatrix represents an orthogonal + * matrix (i.e. orthonormal basis). + */ + byte PROPERTY_ORTHONORMAL = 1<<4; + + /** + * @return the properties of the matrix + */ + int properties(); + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + double m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + double m01(); + + /** + * Return the value of the matrix element at column 0 and row 2. + * + * @return the value of the matrix element + */ + double m02(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + double m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + double m11(); + + /** + * Return the value of the matrix element at column 1 and row 2. + * + * @return the value of the matrix element + */ + double m12(); + + /** + * Return the value of the matrix element at column 2 and row 0. + * + * @return the value of the matrix element + */ + double m20(); + + /** + * Return the value of the matrix element at column 2 and row 1. + * + * @return the value of the matrix element + */ + double m21(); + + /** + * Return the value of the matrix element at column 2 and row 2. + * + * @return the value of the matrix element + */ + double m22(); + + /** + * Return the value of the matrix element at column 3 and row 0. + * + * @return the value of the matrix element + */ + double m30(); + + /** + * Return the value of the matrix element at column 3 and row 1. + * + * @return the value of the matrix element + */ + double m31(); + + /** + * Return the value of the matrix element at column 3 and row 2. + * + * @return the value of the matrix element + */ + double m32(); + + /** + * Get the current values of this matrix and store them into the upper 4x3 submatrix of dest. + *

+ * The other elements of dest will not be modified. + * + * @see Matrix4d#set4x3(Matrix4x3dc) + * + * @param dest + * the destination matrix + * @return dest + */ + Matrix4d get(Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mul(Matrix4x3dc right, Matrix4x3d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the multiplication + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mul(Matrix4x3fc right, Matrix4x3d dest); + + /** + * Multiply this matrix, which is assumed to only contain a translation, by the supplied right matrix and store the result in dest. + *

+ * This method assumes that this matrix only contains a translation. + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4x3d mulTranslation(Matrix4x3dc right, Matrix4x3d dest); + + /** + * Multiply this matrix, which is assumed to only contain a translation, by the supplied right matrix and store the result in dest. + *

+ * This method assumes that this matrix only contains a translation. + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4x3d mulTranslation(Matrix4x3fc right, Matrix4x3d dest); + + /** + * Multiply this orthographic projection matrix by the supplied view matrix + * and store the result in dest. + *

+ * If M is this matrix and V the view matrix, + * then the new matrix will be M * V. So when transforming a + * vector v with the new matrix by using M * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the matrix which to multiply this with + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4x3d mulOrtho(Matrix4x3dc view, Matrix4x3d dest); + + /** + * Multiply this by the 4x3 matrix with the column vectors (rm00, rm01, rm02), + * (rm10, rm11, rm12), (rm20, rm21, rm22) and (0, 0, 0) + * and store the result in dest. + *

+ * If M is this matrix and R the specified matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the R matrix will be applied first! + * + * @param rm00 + * the value of the m00 element + * @param rm01 + * the value of the m01 element + * @param rm02 + * the value of the m02 element + * @param rm10 + * the value of the m10 element + * @param rm11 + * the value of the m11 element + * @param rm12 + * the value of the m12 element + * @param rm20 + * the value of the m20 element + * @param rm21 + * the value of the m21 element + * @param rm22 + * the value of the m22 element + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mul3x3(double rm00, double rm01, double rm02, double rm10, double rm11, double rm12, double rm20, double rm21, double rm22, Matrix4x3d dest); + + /** + * Component-wise add this and other + * by first multiplying each component of other by otherFactor, + * adding that to this and storing the final result in dest. + *

+ * The other components of dest will be set to the ones of this. + *

+ * The matrices this and other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's components + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d fma(Matrix4x3dc other, double otherFactor, Matrix4x3d dest); + + /** + * Component-wise add this and other + * by first multiplying each component of other by otherFactor, + * adding that to this and storing the final result in dest. + *

+ * The other components of dest will be set to the ones of this. + *

+ * The matrices this and other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's components + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d fma(Matrix4x3fc other, double otherFactor, Matrix4x3d dest); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d add(Matrix4x3dc other, Matrix4x3d dest); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d add(Matrix4x3fc other, Matrix4x3d dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d sub(Matrix4x3dc subtrahend, Matrix4x3d dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d sub(Matrix4x3fc subtrahend, Matrix4x3d dest); + + /** + * Component-wise multiply this by other and store the result in dest. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mulComponentWise(Matrix4x3dc other, Matrix4x3d dest); + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + double determinant(); + + /** + * Invert this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d invert(Matrix4x3d dest); + + /** + * Invert this orthographic projection matrix and store the result into the given dest. + *

+ * This method can be used to quickly obtain the inverse of an orthographic projection matrix. + * + * @param dest + * will hold the inverse of this + * @return dest + */ + Matrix4x3d invertOrtho(Matrix4x3d dest); + + /** + * Transpose only the left 3x3 submatrix of this matrix and store the result in dest. + *

+ * All other matrix elements are left unchanged. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d transpose3x3(Matrix4x3d dest); + + /** + * Transpose only the left 3x3 submatrix of this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d transpose3x3(Matrix3d dest); + + /** + * Get only the translation components (m30, m31, m32) of this matrix and store them in the given vector xyz. + * + * @param dest + * will hold the translation components of this matrix + * @return dest + */ + Vector3d getTranslation(Vector3d dest); + + /** + * Get the scaling factors of this matrix for the three base axes. + * + * @param dest + * will hold the scaling factors for x, y and z + * @return dest + */ + Vector3d getScale(Vector3d dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4x3d get(Matrix4x3d dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the first three column vectors of the left 3x3 submatrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaternionf#setFromUnnormalized(Matrix4x3dc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getUnnormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the first three column vectors of the left 3x3 submatrix are normalized. + * + * @see Quaternionf#setFromNormalized(Matrix4x3dc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getNormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the first three column vectors of the left 3x3 submatrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaterniond#setFromUnnormalized(Matrix4x3dc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getUnnormalizedRotation(Quaterniond dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the first three column vectors of the left 3x3 submatrix are normalized. + * + * @see Quaterniond#setFromNormalized(Matrix4x3dc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getNormalizedRotation(Quaterniond dest); + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get(DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given {@link DoubleBuffer}. + * + * @param index + * the absolute position into the {@link DoubleBuffer} + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer get(int index, DoubleBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given + * FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getFloats(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getFloats(int, ByteBuffer) + * + * @param buffer + * will receive the elements of this matrix as float values in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getFloats(ByteBuffer buffer); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the elements of this matrix as float values in column-major order + * @return the passed in buffer + */ + ByteBuffer getFloats(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix4x3dc getToAddress(long address); + + /** + * Store this matrix into the supplied double array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] get(double[] arr, int offset); + + /** + * Store this matrix into the supplied double array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(double[], int)}. + * + * @see #get(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] get(double[] arr); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied float array at the given offset. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given float array. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get(float[] arr, int offset); + + /** + * Store the elements of this matrix as float values in column-major order into the supplied float array. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given float array. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get(float[] arr); + + /** + * Store a 4x4 matrix in column-major order into the supplied array at the given offset, + * where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] get4x4(double[] arr, int offset); + + /** + * Store a 4x4 matrix in column-major order into the supplied array, + * where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * In order to specify an explicit offset into the array, use the method {@link #get4x4(double[], int)}. + * + * @see #get4x4(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] get4x4(double[] arr); + + /** + * Store a 4x4 matrix in column-major order into the supplied array at the given offset, + * where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given float array. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get4x4(float[] arr, int offset); + + /** + * Store a 4x4 matrix in column-major order into the supplied array, + * where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given float array. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get4x4(float[], int)}. + * + * @see #get4x4(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get4x4(float[] arr); + + /** + * Store a 4x4 matrix in column-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}, where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #get4x4(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer get4x4(DoubleBuffer buffer); + + /** + * Store a 4x4 matrix in column-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index, where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + DoubleBuffer get4x4(int index, DoubleBuffer buffer); + + /** + * Store a 4x4 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}, where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get4x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get4x4(ByteBuffer buffer); + + /** + * Store a 4x4 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index, where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get4x4(int index, ByteBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the matrix is stored, use {@link #getTransposed(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, DoubleBuffer) + * + * @param buffer + * will receive the values of this matrix in row-major order at its current position + * @return the passed in buffer + */ + DoubleBuffer getTransposed(DoubleBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this matrix in row-major order + * @return the passed in buffer + */ + DoubleBuffer getTransposed(int index, DoubleBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getTransposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in row-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getTransposed(ByteBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in row-major order + * @return the passed in buffer + */ + ByteBuffer getTransposed(int index, ByteBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #getTransposed(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in row-major order at its current position + * @return the passed in buffer + */ + FloatBuffer getTransposed(FloatBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in row-major order + * @return the passed in buffer + */ + FloatBuffer getTransposed(int index, FloatBuffer buffer); + + /** + * Store this matrix as float values in row-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getTransposedFloats(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposedFloats(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix as float values in row-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getTransposedFloats(ByteBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this matrix storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix as float values in row-major order + * @return the passed in buffer + */ + ByteBuffer getTransposedFloats(int index, ByteBuffer buffer); + + /** + * Store this matrix into the supplied float array in row-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + double[] getTransposed(double[] arr, int offset); + + /** + * Store this matrix into the supplied float array in row-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #getTransposed(double[], int)}. + * + * @see #getTransposed(double[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + double[] getTransposed(double[] arr); + + /** + * Transform/multiply the given vector by this matrix and store the result in that vector. + * + * @see Vector4d#mul(Matrix4x3dc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4d transform(Vector4d v); + + /** + * Transform/multiply the given vector by this matrix and store the result in dest. + * + * @see Vector4d#mul(Matrix4x3dc, Vector4d) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4d transform(Vector4dc v, Vector4d dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=1, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. + *

+ * In order to store the result in another vector, use {@link #transformPosition(Vector3dc, Vector3d)}. + * + * @see #transformPosition(Vector3dc, Vector3d) + * @see #transform(Vector4d) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3d transformPosition(Vector3d v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=1, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector3d)}. + * + * @see #transformPosition(Vector3d) + * @see #transform(Vector4dc, Vector4d) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPosition(Vector3dc v, Vector3d dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector3dc, Vector3d)}. + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3d transformDirection(Vector3d v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector3d)}. + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformDirection(Vector3dc v, Vector3d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d scale(Vector3dc xyz, Matrix4x3d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d scale(double x, double y, double z, Matrix4x3d dest); + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @see #scale(double, double, double, Matrix4x3d) + * + * @param xyz + * the factor for all components + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d scale(double xyz, Matrix4x3d dest); + + /** + * Apply scaling to this matrix by by scaling the X axis by x and the Y axis by y + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d scaleXY(double x, double y, Matrix4x3d dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).scale(sx, sy, sz).translate(-ox, -oy, -oz) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz, Matrix4x3d dest); + + /** + * Apply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).scale(factor).translate(-ox, -oy, -oz) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix4x3d scaleAround(double factor, double ox, double oy, double oz, Matrix4x3d dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d scaleLocal(double x, double y, double z, Matrix4x3d dest); + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the given axis specified as x, y and z components and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v + * , the rotation will be applied first! + * + * @param ang + * the angle is in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotate(double ang, double x, double y, double z, Matrix4x3d dest); + + /** + * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateTranslation(double ang, double x, double y, double z, Matrix4x3d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix while using (ox, oy, oz) as the rotation origin, + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateAround(Quaterniondc quat, double ox, double oy, double oz, Matrix4x3d dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateLocal(double ang, double x, double y, double z, Matrix4x3d dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d translate(Vector3dc offset, Matrix4x3d dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d translate(Vector3fc offset, Matrix4x3d dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d translate(double x, double y, double z, Matrix4x3d dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d translateLocal(Vector3fc offset, Matrix4x3d dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d translateLocal(Vector3dc offset, Matrix4x3d dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d translateLocal(double x, double y, double z, Matrix4x3d dest); + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateX(double ang, Matrix4x3d dest); + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateY(double ang, Matrix4x3d dest); + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateZ(double ang, Matrix4x3d dest); + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX, dest).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateXYZ(double angleX, double angleY, double angleZ, Matrix4x3d dest); + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ, dest).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateZYX(double angleZ, double angleY, double angleX, Matrix4x3d dest); + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY, dest).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateYXZ(double angleY, double angleX, double angleZ, Matrix4x3d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotate(Quaterniondc quat, Matrix4x3d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotate(Quaternionfc quat, Matrix4x3d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateTranslation(Quaterniondc quat, Matrix4x3d dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateTranslation(Quaternionfc quat, Matrix4x3d dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniondc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaterniondc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateLocal(Quaterniondc quat, Matrix4x3d dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateLocal(Quaternionfc quat, Matrix4x3d dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix4x3d) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotate(AxisAngle4f axisAngle, Matrix4x3d dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4d}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4d} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix4x3d) + * + * @param axisAngle + * the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotate(AxisAngle4d axisAngle, Matrix4x3d dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix4x3d) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3d#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotate(double angle, Vector3dc axis, Matrix4x3d dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given angle and axis, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(double, double, double, double, Matrix4x3d) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotate(double angle, Vector3fc axis, Matrix4x3d dest); + + /** + * Get the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param dest + * will hold the row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + Vector4d getRow(int row, Vector4d dest) throws IndexOutOfBoundsException; + + /** + * Get the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param dest + * will hold the column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + Vector3d getColumn(int column, Vector3d dest) throws IndexOutOfBoundsException; + + /** + * Compute a normal matrix from the left 3x3 submatrix of this + * and store it into the left 3x3 submatrix of dest. + * All other values of dest will be set to identity. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d normal(Matrix4x3d dest); + + /** + * Compute a normal matrix from the left 3x3 submatrix of this + * and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d normal(Matrix3d dest); + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this + * and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d cofactor3x3(Matrix3d dest); + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this + * and store it into dest. + * All other values of dest will be set to identity. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix4x3d)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d cofactor3x3(Matrix4x3d dest); + + /** + * Normalize the left 3x3 submatrix of this matrix and store the result in dest. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d normalize3x3(Matrix4x3d dest); + + /** + * Normalize the left 3x3 submatrix of this matrix and store the result in dest. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3d normalize3x3(Matrix3d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0 and store the result in dest. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d reflect(double a, double b, double c, double d, Matrix4x3d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d reflect(double nx, double ny, double nz, double px, double py, double pz, Matrix4x3d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation and a point on the plane, and store the result in dest. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaterniondc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation + * @param point + * a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d reflect(Quaterniondc orientation, Vector3dc point, Matrix4x3d dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d reflect(Vector3dc normal, Vector3dc point, Matrix4x3d dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d ortho(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4x3d dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d ortho(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4x3d dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, boolean zZeroToOne, Matrix4x3d dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, Matrix4x3d dest); + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, boolean, Matrix4x3d) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4x3d orthoSymmetric(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4x3d dest); + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4x3d) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d orthoSymmetric(double width, double height, double zNear, double zFar, Matrix4x3d dest); + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, boolean, Matrix4x3d) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4x3d orthoSymmetricLH(double width, double height, double zNear, double zFar, boolean zZeroToOne, Matrix4x3d dest); + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4x3d) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d orthoSymmetricLH(double width, double height, double zNear, double zFar, Matrix4x3d dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4x3d) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(double, double, double, double, double, double, Matrix4x3d) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d ortho2D(double left, double right, double bottom, double top, Matrix4x3d dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4x3d) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(double, double, double, double, double, double, Matrix4x3d) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d ortho2DLH(double left, double right, double bottom, double top, Matrix4x3d dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3dc, Vector3dc, Vector3dc, Matrix4x3d) lookAt} + * with eye = (0, 0, 0) and center = dir. + * + * @see #lookAlong(double, double, double, double, double, double, Matrix4x3d) + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc, Matrix4x3d) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d lookAlong(Vector3dc dir, Vector3dc up, Matrix4x3d dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(double, double, double, double, double, double, double, double, double, Matrix4x3d) lookAt()} + * with eye = (0, 0, 0) and center = dir. + * + * @see #lookAt(double, double, double, double, double, double, double, double, double, Matrix4x3d) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix4x3d dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAt(double, double, double, double, double, double, double, double, double, Matrix4x3d) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d lookAt(Vector3dc eye, Vector3dc center, Vector3dc up, Matrix4x3d dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAt(Vector3dc, Vector3dc, Vector3dc, Matrix4x3d) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d lookAt(double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ, Matrix4x3d dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAtLH(double, double, double, double, double, double, double, double, double, Matrix4x3d) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d lookAtLH(Vector3dc eye, Vector3dc center, Vector3dc up, Matrix4x3d dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAtLH(Vector3dc, Vector3dc, Vector3dc, Matrix4x3d) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d lookAtLH(double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ, Matrix4x3d dest); + + /** + * Calculate a frustum plane of this matrix, which + * can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given dest. + *

+ * Generally, this method computes the frustum plane in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * The plane normal, which is (a, b, c), is directed "inwards" of the frustum. + * Any plane/point test using a*x + b*y + c*z + d therefore will yield a result greater than zero + * if the point is within the frustum (i.e. at the positive side of the frustum plane). + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param which + * one of the six possible planes, given as numeric constants + * {@link #PLANE_NX}, {@link #PLANE_PX}, + * {@link #PLANE_NY}, {@link #PLANE_PY}, + * {@link #PLANE_NZ} and {@link #PLANE_PZ} + * @param dest + * will hold the computed plane equation. + * The plane equation will be normalized, meaning that (a, b, c) will be a unit vector + * @return dest + */ + Vector4d frustumPlane(int which, Vector4d dest); + + /** + * Obtain the direction of +Z before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +Z by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3d inv = new Matrix4x3d(this).invert();
+     * inv.transformDirection(dir.set(0, 0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveZ(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3d positiveZ(Vector3d dir); + + /** + * Obtain the direction of +Z before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +Z by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3d inv = new Matrix4x3d(this).transpose();
+     * inv.transformDirection(dir.set(0, 0, 1)).normalize();
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3d normalizedPositiveZ(Vector3d dir); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3d inv = new Matrix4x3d(this).invert();
+     * inv.transformDirection(dir.set(1, 0, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3d positiveX(Vector3d dir); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3d inv = new Matrix4x3d(this).transpose();
+     * inv.transformDirection(dir.set(1, 0, 0)).normalize();
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3d normalizedPositiveX(Vector3d dir); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3d inv = new Matrix4x3d(this).invert();
+     * inv.transformDirection(dir.set(0, 1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector3d)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3d positiveY(Vector3d dir); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3d inv = new Matrix4x3d(this).transpose();
+     * inv.transformDirection(dir.set(0, 1, 0)).normalize();
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3d normalizedPositiveY(Vector3d dir); + + /** + * Obtain the position that gets transformed to the origin by this matrix. + * This can be used to get the position of the "camera" from a given view transformation matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3f inv = new Matrix4x3f(this).invert();
+     * inv.transformPosition(origin.set(0, 0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + Vector3d origin(Vector3d origin); + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction light + * and store the result in dest. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param light + * the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d shadow(Vector4dc light, double a, double b, double c, double d, Matrix4x3d dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW) + * and store the result in dest. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param lightX + * the x-component of the light's vector + * @param lightY + * the y-component of the light's vector + * @param lightZ + * the z-component of the light's vector + * @param lightW + * the w-component of the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d, Matrix4x3d dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction light + * and store the result in dest. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param light + * the light's vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d shadow(Vector4dc light, Matrix4x3dc planeTransform, Matrix4x3d dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW) + * and store the result in dest. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param lightX + * the x-component of the light vector + * @param lightY + * the y-component of the light vector + * @param lightZ + * the z-component of the light vector + * @param lightW + * the w-component of the light vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4x3dc planeTransform, Matrix4x3d dest); + + /** + * Apply a picking transformation to this matrix using the given window coordinates (x, y) as the pick center + * and the given (width, height) as the size of the picking region in window coordinates, and store the result + * in dest. + * + * @param x + * the x coordinate of the picking region center in window coordinates + * @param y + * the y coordinate of the picking region center in window coordinates + * @param width + * the width of the picking region in window coordinates + * @param height + * the height of the picking region in window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4x3d pick(double x, double y, double width, double height, int[] viewport, Matrix4x3d dest); + + /** + * Apply an arcball view transformation to this matrix with the given radius and center (centerX, centerY, centerZ) + * position of the arcball and the specified X and Y rotation angles, and store the result in dest. + *

+ * This method is equivalent to calling: translate(0, 0, -radius, dest).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ) + * + * @param radius + * the arcball radius + * @param centerX + * the x coordinate of the center position of the arcball + * @param centerY + * the y coordinate of the center position of the arcball + * @param centerZ + * the z coordinate of the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY, Matrix4x3d dest); + + /** + * Apply an arcball view transformation to this matrix with the given radius and center + * position of the arcball and the specified X and Y rotation angles, and store the result in dest. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z) + * + * @param radius + * the arcball radius + * @param center + * the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d arcball(double radius, Vector3dc center, double angleX, double angleY, Matrix4x3d dest); + + /** + * Transform the axis-aligned box given as the minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ) + * by this matrix and compute the axis-aligned box of the result whose minimum corner is stored in outMin + * and maximum corner stored in outMax. + *

+ * Reference: http://dev.theomader.com + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the y coordinate of the maximum corner of the axis-aligned box + * @param outMin + * will hold the minimum corner of the resulting axis-aligned box + * @param outMax + * will hold the maximum corner of the resulting axis-aligned box + * @return this + */ + Matrix4x3d transformAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vector3d outMin, Vector3d outMax); + + /** + * Transform the axis-aligned box given as the minimum corner min and maximum corner max + * by this matrix and compute the axis-aligned box of the result whose minimum corner is stored in outMin + * and maximum corner stored in outMax. + * + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param outMin + * will hold the minimum corner of the resulting axis-aligned box + * @param outMax + * will hold the maximum corner of the resulting axis-aligned box + * @return this + */ + Matrix4x3d transformAab(Vector3dc min, Vector3dc max, Vector3d outMin, Vector3d outMax); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d lerp(Matrix4x3dc other, double t, Matrix4x3d dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the -z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mul(new Matrix4x3d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invert(), dest) + * + * @see #rotateTowards(double, double, double, double, double, double, Matrix4x3d) + * + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateTowards(Vector3dc dir, Vector3dc up, Matrix4x3d dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the -z axis with (dirX, dirY, dirZ) + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mul(new Matrix4x3d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invert(), dest) + * + * @see #rotateTowards(Vector3dc, Vector3dc, Matrix4x3d) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Matrix4x3d dest); + + /** + * Extract the Euler angles from the rotation represented by the left 3x3 submatrix of this + * and store the extracted Euler angles in dest. + *

+ * This method assumes that the left 3x3 submatrix of this only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3d#x} field, the angle around Y in the {@link Vector3d#y} + * field and the angle around Z in the {@link Vector3d#z} field of the supplied {@link Vector3d} instance. + *

+ * Note that the returned Euler angles must be applied in the order X * Y * Z to obtain the identical matrix. + * This means that calling {@link Matrix4x3d#rotateXYZ(double, double, double)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix4x3d m = ...; // <- matrix only representing rotation
+     * Matrix4x3d n = new Matrix4x3d();
+     * n.rotateXYZ(m.getEulerAnglesXYZ(new Vector3d()));
+     * 
+ *

+ * Reference: http://en.wikipedia.org/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3d getEulerAnglesXYZ(Vector3d dest); + + /** + * Extract the Euler angles from the rotation represented by the left 3x3 submatrix of this + * and store the extracted Euler angles in dest. + *

+ * This method assumes that the left 3x3 submatrix of this only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3d#x} field, the angle around Y in the {@link Vector3d#y} + * field and the angle around Z in the {@link Vector3d#z} field of the supplied {@link Vector3d} instance. + *

+ * Note that the returned Euler angles must be applied in the order Z * Y * X to obtain the identical matrix. + * This means that calling {@link Matrix4x3d#rotateZYX(double, double, double)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix4x3d m = ...; // <- matrix only representing rotation
+     * Matrix4x3d n = new Matrix4x3d();
+     * n.rotateZYX(m.getEulerAnglesZYX(new Vector3d()));
+     * 
+ *

+ * Reference: http://en.wikipedia.org/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3d getEulerAnglesZYX(Vector3d dest); + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d obliqueZ(double a, double b, Matrix4x3d dest); + + /** + * Multiply this by the matrix + *
+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapXZY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapXZnY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapXnYnZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapXnZY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapXnZnY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapYXZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapYXnZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapYZX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapYZnX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapYnXZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapYnXnZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapYnZX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapYnZnX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 0 0 1 0
+     * 1 0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapZXY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapZXnY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 0 1 0 0
+     * 1 0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapZYX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 1 0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapZYnX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 1  0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapZnXY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapZnXnY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapZnYX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapZnYnX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnXYnZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnXZY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnXZnY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnXnYZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnXnYnZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0 -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnXnZY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnXnZnY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnYXZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnYXnZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnYZX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnYZnX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnYnXZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnYnXnZ(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0 -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnYnZX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0 -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnYnZnX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     *  0 0 1 0
+     * -1 0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnZXY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     *  0 0 -1 0
+     * -1 0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnZXnY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     *  0 1 0 0
+     * -1 0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnZYX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     *  0 1  0 0
+     * -1 0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnZYnX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     *  0  0 1 0
+     * -1  0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnZnXY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * -1  0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnZnXnY(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     *  0 -1 0 0
+     * -1  0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnZnYX(Matrix4x3d dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * -1  0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d mapnZnYnX(Matrix4x3d dest); + + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d negateX(Matrix4x3d dest); + + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d negateY(Matrix4x3d dest); + + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3d negateZ(Matrix4x3d dest); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix4x3dc m, double delta); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3f.java new file mode 100644 index 000000000..c3c6865e7 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3f.java @@ -0,0 +1,9838 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + + +/** + * Contains the definition of an affine 4x3 matrix (4 columns, 3 rows) of floats, and associated functions to transform + * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this: + *

+ * m00 m10 m20 m30
+ * m01 m11 m21 m31
+ * m02 m12 m22 m32
+ * + * @author Richard Greenlees + * @author Kai Burjack + */ +public class Matrix4x3f implements Externalizable, Cloneable, Matrix4x3fc { + + private static final long serialVersionUID = 1L; + + float m00, m01, m02; + float m10, m11, m12; + float m20, m21, m22; + float m30, m31, m32; + + int properties; + + /** + * Create a new {@link Matrix4x3f} and set it to {@link #identity() identity}. + */ + public Matrix4x3f() { + m00 = 1.0f; + m11 = 1.0f; + m22 = 1.0f; + properties = PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + } + + /** + * Create a new {@link Matrix4x3f} by setting its left 3x3 submatrix to the values of the given {@link Matrix3fc} + * and the rest to identity. + * + * @param mat + * the {@link Matrix3fc} + */ + public Matrix4x3f(Matrix3fc mat) { + set(mat); + } + + /** + * Create a new {@link Matrix4x3f} and make it a copy of the given matrix. + * + * @param mat + * the {@link Matrix4x3fc} to copy the values from + */ + public Matrix4x3f(Matrix4x3fc mat) { + set(mat); + } + + /** + * Create a new 4x4 matrix using the supplied float values. + * + * @param m00 + * the value of m00 + * @param m01 + * the value of m01 + * @param m02 + * the value of m02 + * @param m10 + * the value of m10 + * @param m11 + * the value of m11 + * @param m12 + * the value of m12 + * @param m20 + * the value of m20 + * @param m21 + * the value of m21 + * @param m22 + * the value of m22 + * @param m30 + * the value of m30 + * @param m31 + * the value of m31 + * @param m32 + * the value of m32 + */ + public Matrix4x3f(float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22, + float m30, float m31, float m32) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + this.m30 = m30; + this.m31 = m31; + this.m32 = m32; + determineProperties(); + } + + /** + * Create a new {@link Matrix4x3f} by reading its 12 float components from the given {@link FloatBuffer} + * at the buffer's current position. + *

+ * That FloatBuffer is expected to hold the values in column-major order. + *

+ * The buffer's position will not be changed by this method. + * + * @param buffer + * the {@link FloatBuffer} to read the matrix values from + */ + public Matrix4x3f(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + determineProperties(); + } + + /** + * Create a new {@link Matrix4x3f} and initialize its four columns using the supplied vectors. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @param col3 + * the fourth column + */ + public Matrix4x3f(Vector3fc col0, Vector3fc col1, Vector3fc col2, Vector3fc col3) { + set(col0, col1, col2, col3). + determineProperties(); + } + + /** + * Assume the given properties about this matrix. + *

+ * Use one or multiple of 0, {@link Matrix4x3fc#PROPERTY_IDENTITY}, + * {@link Matrix4x3fc#PROPERTY_TRANSLATION}, {@link Matrix4x3fc#PROPERTY_ORTHONORMAL}. + * + * @param properties + * bitset of the properties to assume about this matrix + * @return this + */ + public Matrix4x3f assume(int properties) { + this.properties = properties; + return this; + } + + /** + * Compute and set the matrix properties returned by {@link #properties()} based + * on the current matrix element values. + * + * @return this + */ + public Matrix4x3f determineProperties() { + int properties = 0; + if (m00 == 1.0f && m01 == 0.0f && m02 == 0.0f && m10 == 0.0f && m11 == 1.0f && m12 == 0.0f + && m20 == 0.0f && m21 == 0.0f && m22 == 1.0f) { + properties |= PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + if (m30 == 0.0f && m31 == 0.0f && m32 == 0.0f) + properties |= PROPERTY_IDENTITY; + } + /* + * We do not determine orthogonality, since it would require arbitrary epsilons + * and is rather expensive (6 dot products) in the worst case. + */ + this.properties = properties; + return this; + } + + public int properties() { + return properties; + } + + public float m00() { + return m00; + } + public float m01() { + return m01; + } + public float m02() { + return m02; + } + public float m10() { + return m10; + } + public float m11() { + return m11; + } + public float m12() { + return m12; + } + public float m20() { + return m20; + } + public float m21() { + return m21; + } + public float m22() { + return m22; + } + public float m30() { + return m30; + } + public float m31() { + return m31; + } + public float m32() { + return m32; + } + + /** + * Set the value of the matrix element at column 0 and row 0. + * + * @param m00 + * the new value + * @return this + */ + public Matrix4x3f m00(float m00) { + this.m00 = m00; + properties &= ~PROPERTY_ORTHONORMAL; + if (m00 != 1.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1. + * + * @param m01 + * the new value + * @return this + */ + public Matrix4x3f m01(float m01) { + this.m01 = m01; + properties &= ~PROPERTY_ORTHONORMAL; + if (m01 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2. + * + * @param m02 + * the new value + * @return this + */ + public Matrix4x3f m02(float m02) { + this.m02 = m02; + properties &= ~PROPERTY_ORTHONORMAL; + if (m02 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0. + * + * @param m10 + * the new value + * @return this + */ + public Matrix4x3f m10(float m10) { + this.m10 = m10; + properties &= ~PROPERTY_ORTHONORMAL; + if (m10 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1. + * + * @param m11 + * the new value + * @return this + */ + public Matrix4x3f m11(float m11) { + this.m11 = m11; + properties &= ~PROPERTY_ORTHONORMAL; + if (m11 != 1.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2. + * + * @param m12 + * the new value + * @return this + */ + public Matrix4x3f m12(float m12) { + this.m12 = m12; + properties &= ~PROPERTY_ORTHONORMAL; + if (m12 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0. + * + * @param m20 + * the new value + * @return this + */ + public Matrix4x3f m20(float m20) { + this.m20 = m20; + properties &= ~PROPERTY_ORTHONORMAL; + if (m20 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1. + * + * @param m21 + * the new value + * @return this + */ + public Matrix4x3f m21(float m21) { + this.m21 = m21; + properties &= ~PROPERTY_ORTHONORMAL; + if (m21 != 0.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2. + * + * @param m22 + * the new value + * @return this + */ + public Matrix4x3f m22(float m22) { + this.m22 = m22; + properties &= ~PROPERTY_ORTHONORMAL; + if (m22 != 1.0f) + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + /** + * Set the value of the matrix element at column 3 and row 0. + * + * @param m30 + * the new value + * @return this + */ + public Matrix4x3f m30(float m30) { + this.m30 = m30; + if (m30 != 0.0f) + properties &= ~PROPERTY_IDENTITY; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 1. + * + * @param m31 + * the new value + * @return this + */ + public Matrix4x3f m31(float m31) { + this.m31 = m31; + if (m31 != 0.0f) + properties &= ~PROPERTY_IDENTITY; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 2. + * + * @param m32 + * the new value + * @return this + */ + public Matrix4x3f m32(float m32) { + this.m32 = m32; + if (m32 != 0.0f) + properties &= ~PROPERTY_IDENTITY; + return this; + } + + Matrix4x3f _properties(int properties) { + this.properties = properties; + return this; + } + + /** + * Set the value of the matrix element at column 0 and row 0 without updating the properties of the matrix. + * + * @param m00 + * the new value + * @return this + */ + Matrix4x3f _m00(float m00) { + this.m00 = m00; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 1 without updating the properties of the matrix. + * + * @param m01 + * the new value + * @return this + */ + Matrix4x3f _m01(float m01) { + this.m01 = m01; + return this; + } + /** + * Set the value of the matrix element at column 0 and row 2 without updating the properties of the matrix. + * + * @param m02 + * the new value + * @return this + */ + Matrix4x3f _m02(float m02) { + this.m02 = m02; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 0 without updating the properties of the matrix. + * + * @param m10 + * the new value + * @return this + */ + Matrix4x3f _m10(float m10) { + this.m10 = m10; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 1 without updating the properties of the matrix. + * + * @param m11 + * the new value + * @return this + */ + Matrix4x3f _m11(float m11) { + this.m11 = m11; + return this; + } + /** + * Set the value of the matrix element at column 1 and row 2 without updating the properties of the matrix. + * + * @param m12 + * the new value + * @return this + */ + Matrix4x3f _m12(float m12) { + this.m12 = m12; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 0 without updating the properties of the matrix. + * + * @param m20 + * the new value + * @return this + */ + Matrix4x3f _m20(float m20) { + this.m20 = m20; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 1 without updating the properties of the matrix. + * + * @param m21 + * the new value + * @return this + */ + Matrix4x3f _m21(float m21) { + this.m21 = m21; + return this; + } + /** + * Set the value of the matrix element at column 2 and row 2 without updating the properties of the matrix. + * + * @param m22 + * the new value + * @return this + */ + Matrix4x3f _m22(float m22) { + this.m22 = m22; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 0 without updating the properties of the matrix. + * + * @param m30 + * the new value + * @return this + */ + Matrix4x3f _m30(float m30) { + this.m30 = m30; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 1 without updating the properties of the matrix. + * + * @param m31 + * the new value + * @return this + */ + Matrix4x3f _m31(float m31) { + this.m31 = m31; + return this; + } + /** + * Set the value of the matrix element at column 3 and row 2 without updating the properties of the matrix. + * + * @param m32 + * the new value + * @return this + */ + Matrix4x3f _m32(float m32) { + this.m32 = m32; + return this; + } + + /** + * Reset this matrix to the identity. + *

+ * Please note that if a call to {@link #identity()} is immediately followed by a call to: + * {@link #translate(float, float, float) translate}, + * {@link #rotate(float, float, float, float) rotate}, + * {@link #scale(float, float, float) scale}, + * {@link #ortho(float, float, float, float, float, float) ortho}, + * {@link #ortho2D(float, float, float, float) ortho2D}, + * {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt}, + * {@link #lookAlong(float, float, float, float, float, float) lookAlong}, + * or any of their overloads, then the call to {@link #identity()} can be omitted and the subsequent call replaced with: + * {@link #translation(float, float, float) translation}, + * {@link #rotation(float, float, float, float) rotation}, + * {@link #scaling(float, float, float) scaling}, + * {@link #setOrtho(float, float, float, float, float, float) setOrtho}, + * {@link #setOrtho2D(float, float, float, float) setOrtho2D}, + * {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt}, + * {@link #setLookAlong(float, float, float, float, float, float) setLookAlong}, + * or any of their overloads. + * + * @return this + */ + public Matrix4x3f identity() { + if ((properties & PROPERTY_IDENTITY) != 0) + return this; + MemUtil.INSTANCE.identity(this); + properties = PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Store the values of the given matrix m into this matrix. + * + * @see #Matrix4x3f(Matrix4x3fc) + * @see #get(Matrix4x3f) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4x3f set(Matrix4x3fc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + m30 = m.m30(); + m31 = m.m31(); + m32 = m.m32(); + properties = m.properties(); + return this; + } + + /** + * Store the values of the upper 4x3 submatrix of m into this matrix. + * + * @see Matrix4fc#get4x3(Matrix4x3f) + * + * @param m + * the matrix to copy the values from + * @return this + */ + public Matrix4x3f set(Matrix4fc m) { + m00 = m.m00(); + m01 = m.m01(); + m02 = m.m02(); + m10 = m.m10(); + m11 = m.m11(); + m12 = m.m12(); + m20 = m.m20(); + m21 = m.m21(); + m22 = m.m22(); + m30 = m.m30(); + m31 = m.m31(); + m32 = m.m32(); + properties = m.properties() & (PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return this; + } + + public Matrix4f get(Matrix4f dest) { + return dest.set4x3(this); + } + + public Matrix4d get(Matrix4d dest) { + return dest.set4x3(this); + } + + /** + * Set the left 3x3 submatrix of this {@link Matrix4x3f} to the given {@link Matrix3fc} + * and the rest to identity. + * + * @see #Matrix4x3f(Matrix3fc) + * + * @param mat + * the {@link Matrix3fc} + * @return this + */ + public Matrix4x3f set(Matrix3fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + return determineProperties(); + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + * @return this + */ + public Matrix4x3f set(AxisAngle4f axisAngle) { + float x = axisAngle.x; + float y = axisAngle.y; + float z = axisAngle.z; + float angle = axisAngle.angle; + float n = Math.sqrt(x*x + y*y + z*z); + n = 1/n; + x *= n; + y *= n; + z *= n; + float s = Math.sin(angle); + float c = Math.cosFromSin(s, angle); + float omc = 1.0f - c; + m00 = (float)(c + x*x*omc); + m11 = (float)(c + y*y*omc); + m22 = (float)(c + z*z*omc); + float tmp1 = x*y*omc; + float tmp2 = z*s; + m10 = (float)(tmp1 - tmp2); + m01 = (float)(tmp1 + tmp2); + tmp1 = x*z*omc; + tmp2 = y*s; + m20 = (float)(tmp1 + tmp2); + m02 = (float)(tmp1 - tmp2); + tmp1 = y*z*omc; + tmp2 = x*s; + m21 = (float)(tmp1 - tmp2); + m12 = (float)(tmp1 + tmp2); + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + * @return this + */ + public Matrix4x3f set(AxisAngle4d axisAngle) { + double x = axisAngle.x; + double y = axisAngle.y; + double z = axisAngle.z; + double angle = axisAngle.angle; + double n = Math.sqrt(x*x + y*y + z*z); + n = 1/n; + x *= n; + y *= n; + z *= n; + double s = Math.sin(angle); + double c = Math.cosFromSin(s, angle); + double omc = 1.0 - c; + m00 = (float)(c + x*x*omc); + m11 = (float)(c + y*y*omc); + m22 = (float)(c + z*z*omc); + double tmp1 = x*y*omc; + double tmp2 = z*s; + m10 = (float)(tmp1 - tmp2); + m01 = (float)(tmp1 + tmp2); + tmp1 = x*z*omc; + tmp2 = y*s; + m20 = (float)(tmp1 + tmp2); + m02 = (float)(tmp1 - tmp2); + tmp1 = y*z*omc; + tmp2 = x*s; + m21 = (float)(tmp1 - tmp2); + m12 = (float)(tmp1 + tmp2); + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaternionfc}. + *

+ * This method is equivalent to calling: rotation(q) + * + * @see #rotation(Quaternionfc) + * + * @param q + * the {@link Quaternionfc} + * @return this + */ + public Matrix4x3f set(Quaternionfc q) { + return rotation(q); + } + + /** + * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaterniondc}. + *

+ * This method is equivalent to calling: rotation(q) + * + * @param q + * the {@link Quaterniondc} + * @return this + */ + public Matrix4x3f set(Quaterniondc q) { + double w2 = q.w() * q.w(); + double x2 = q.x() * q.x(); + double y2 = q.y() * q.y(); + double z2 = q.z() * q.z(); + double zw = q.z() * q.w(); + double xy = q.x() * q.y(); + double xz = q.x() * q.z(); + double yw = q.y() * q.w(); + double yz = q.y() * q.z(); + double xw = q.x() * q.w(); + m00 = (float) (w2 + x2 - z2 - y2); + m01 = (float) (xy + zw + zw + xy); + m02 = (float) (xz - yw + xz - yw); + m10 = (float) (-zw + xy - zw + xy); + m11 = (float) (y2 - z2 + w2 - x2); + m12 = (float) (yz + yz + xw + xw); + m20 = (float) (yw + xz + xz + yw); + m21 = (float) (yz + yz - xw - xw); + m22 = (float) (z2 - y2 - x2 + w2); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set the four columns of this matrix to the supplied vectors, respectively. + * + * @param col0 + * the first column + * @param col1 + * the second column + * @param col2 + * the third column + * @param col3 + * the fourth column + * @return this + */ + public Matrix4x3f set(Vector3fc col0, Vector3fc col1, Vector3fc col2, Vector3fc col3) { + this.m00 = col0.x(); + this.m01 = col0.y(); + this.m02 = col0.z(); + this.m10 = col1.x(); + this.m11 = col1.y(); + this.m12 = col1.z(); + this.m20 = col2.x(); + this.m21 = col2.y(); + this.m22 = col2.z(); + this.m30 = col3.x(); + this.m31 = col3.y(); + this.m32 = col3.z(); + return determineProperties(); + } + + /** + * Set the left 3x3 submatrix of this {@link Matrix4x3f} to that of the given {@link Matrix4x3fc} + * and don't change the other elements. + * + * @param mat + * the {@link Matrix4x3fc} + * @return this + */ + public Matrix4x3f set3x3(Matrix4x3fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + properties &= mat.properties(); + return this; + } + + /** + * Multiply this matrix by the supplied right matrix and store the result in this. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @return this + */ + public Matrix4x3f mul(Matrix4x3fc right) { + return mul(right, this); + } + + public Matrix4x3f mul(Matrix4x3fc right, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.set(right); + else if ((right.properties() & PROPERTY_IDENTITY) != 0) + return dest.set(this); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return mulTranslation(right, dest); + return mulGeneric(right, dest); + } + private Matrix4x3f mulGeneric(Matrix4x3fc right, Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + float m20 = this.m20, m21 = this.m21, m22 = this.m22; + float rm00 = right.m00(), rm01 = right.m01(), rm02 = right.m02(); + float rm10 = right.m10(), rm11 = right.m11(), rm12 = right.m12(); + float rm20 = right.m20(), rm21 = right.m21(), rm22 = right.m22(); + float rm30 = right.m30(), rm31 = right.m31(), rm32 = right.m32(); + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30)))) + ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31)))) + ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32)))) + ._properties(properties & right.properties() & PROPERTY_ORTHONORMAL); + } + + public Matrix4x3f mulTranslation(Matrix4x3fc right, Matrix4x3f dest) { + return dest + ._m00(right.m00()) + ._m01(right.m01()) + ._m02(right.m02()) + ._m10(right.m10()) + ._m11(right.m11()) + ._m12(right.m12()) + ._m20(right.m20()) + ._m21(right.m21()) + ._m22(right.m22()) + ._m30(right.m30() + m30) + ._m31(right.m31() + m31) + ._m32(right.m32() + m32) + ._properties(right.properties() & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this orthographic projection matrix by the supplied view matrix. + *

+ * If M is this matrix and V the view matrix, + * then the new matrix will be M * V. So when transforming a + * vector v with the new matrix by using M * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the matrix which to multiply this with + * @return this + */ + public Matrix4x3f mulOrtho(Matrix4x3fc view) { + return mulOrtho(view, this); + } + + public Matrix4x3f mulOrtho(Matrix4x3fc view, Matrix4x3f dest) { + float nm00 = m00 * view.m00(); + float nm01 = m11 * view.m01(); + float nm02 = m22 * view.m02(); + float nm10 = m00 * view.m10(); + float nm11 = m11 * view.m11(); + float nm12 = m22 * view.m12(); + float nm20 = m00 * view.m20(); + float nm21 = m11 * view.m21(); + float nm22 = m22 * view.m22(); + float nm30 = m00 * view.m30() + m30; + float nm31 = m11 * view.m31() + m31; + float nm32 = m22 * view.m32() + m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = (this.properties & view.properties() & PROPERTY_ORTHONORMAL); + return dest; + } + + /** + * Multiply this by the 4x3 matrix with the column vectors (rm00, rm01, rm02), + * (rm10, rm11, rm12), (rm20, rm21, rm22) and (0, 0, 0). + *

+ * If M is this matrix and R the specified matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the R matrix will be applied first! + * + * @param rm00 + * the value of the m00 element + * @param rm01 + * the value of the m01 element + * @param rm02 + * the value of the m02 element + * @param rm10 + * the value of the m10 element + * @param rm11 + * the value of the m11 element + * @param rm12 + * the value of the m12 element + * @param rm20 + * the value of the m20 element + * @param rm21 + * the value of the m21 element + * @param rm22 + * the value of the m22 element + * @return this + */ + public Matrix4x3f mul3x3( + float rm00, float rm01, float rm02, + float rm10, float rm11, float rm12, + float rm20, float rm21, float rm22) { + return mul3x3(rm00, rm01, rm02, rm10, rm11, rm12, rm20, rm21, rm22, this); + } + public Matrix4x3f mul3x3( + float rm00, float rm01, float rm02, + float rm10, float rm11, float rm12, + float rm20, float rm21, float rm22, + Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + float m20 = this.m20, m21 = this.m21, m22 = this.m22; + return dest + ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02))) + ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02))) + ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02))) + ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12))) + ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12))) + ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12))) + ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22))) + ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22))) + ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22))) + ._m30(m30) + ._m31(m31) + ._m32(m32) + ._properties(0); + } + + /** + * Component-wise add this and other + * by first multiplying each component of other by otherFactor and + * adding that result to this. + *

+ * The matrix other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's components + * @return this + */ + public Matrix4x3f fma(Matrix4x3fc other, float otherFactor) { + return fma(other, otherFactor, this); + } + + public Matrix4x3f fma(Matrix4x3fc other, float otherFactor, Matrix4x3f dest) { + dest + ._m00(Math.fma(other.m00(), otherFactor, m00)) + ._m01(Math.fma(other.m01(), otherFactor, m01)) + ._m02(Math.fma(other.m02(), otherFactor, m02)) + ._m10(Math.fma(other.m10(), otherFactor, m10)) + ._m11(Math.fma(other.m11(), otherFactor, m11)) + ._m12(Math.fma(other.m12(), otherFactor, m12)) + ._m20(Math.fma(other.m20(), otherFactor, m20)) + ._m21(Math.fma(other.m21(), otherFactor, m21)) + ._m22(Math.fma(other.m22(), otherFactor, m22)) + ._m30(Math.fma(other.m30(), otherFactor, m30)) + ._m31(Math.fma(other.m31(), otherFactor, m31)) + ._m32(Math.fma(other.m32(), otherFactor, m32)) + ._properties(0); + return dest; + } + + /** + * Component-wise add this and other. + * + * @param other + * the other addend + * @return this + */ + public Matrix4x3f add(Matrix4x3fc other) { + return add(other, this); + } + + public Matrix4x3f add(Matrix4x3fc other, Matrix4x3f dest) { + dest.m00 = m00 + other.m00(); + dest.m01 = m01 + other.m01(); + dest.m02 = m02 + other.m02(); + dest.m10 = m10 + other.m10(); + dest.m11 = m11 + other.m11(); + dest.m12 = m12 + other.m12(); + dest.m20 = m20 + other.m20(); + dest.m21 = m21 + other.m21(); + dest.m22 = m22 + other.m22(); + dest.m30 = m30 + other.m30(); + dest.m31 = m31 + other.m31(); + dest.m32 = m32 + other.m32(); + dest.properties = 0; + return dest; + } + + /** + * Component-wise subtract subtrahend from this. + * + * @param subtrahend + * the subtrahend + * @return this + */ + public Matrix4x3f sub(Matrix4x3fc subtrahend) { + return sub(subtrahend, this); + } + + public Matrix4x3f sub(Matrix4x3fc subtrahend, Matrix4x3f dest) { + dest.m00 = m00 - subtrahend.m00(); + dest.m01 = m01 - subtrahend.m01(); + dest.m02 = m02 - subtrahend.m02(); + dest.m10 = m10 - subtrahend.m10(); + dest.m11 = m11 - subtrahend.m11(); + dest.m12 = m12 - subtrahend.m12(); + dest.m20 = m20 - subtrahend.m20(); + dest.m21 = m21 - subtrahend.m21(); + dest.m22 = m22 - subtrahend.m22(); + dest.m30 = m30 - subtrahend.m30(); + dest.m31 = m31 - subtrahend.m31(); + dest.m32 = m32 - subtrahend.m32(); + dest.properties = 0; + return dest; + } + + /** + * Component-wise multiply this by other. + * + * @param other + * the other matrix + * @return this + */ + public Matrix4x3f mulComponentWise(Matrix4x3fc other) { + return mulComponentWise(other, this); + } + + public Matrix4x3f mulComponentWise(Matrix4x3fc other, Matrix4x3f dest) { + dest.m00 = m00 * other.m00(); + dest.m01 = m01 * other.m01(); + dest.m02 = m02 * other.m02(); + dest.m10 = m10 * other.m10(); + dest.m11 = m11 * other.m11(); + dest.m12 = m12 * other.m12(); + dest.m20 = m20 * other.m20(); + dest.m21 = m21 * other.m21(); + dest.m22 = m22 * other.m22(); + dest.m30 = m30 * other.m30(); + dest.m31 = m31 * other.m31(); + dest.m32 = m32 * other.m32(); + dest.properties = 0; + return dest; + } + + /** + * Set the values within this matrix to the supplied float values. The matrix will look like this:

+ * + * m00, m10, m20, m30
+ * m01, m11, m21, m31
+ * m02, m12, m22, m32
+ * + * @param m00 + * the new value of m00 + * @param m01 + * the new value of m01 + * @param m02 + * the new value of m02 + * @param m10 + * the new value of m10 + * @param m11 + * the new value of m11 + * @param m12 + * the new value of m12 + * @param m20 + * the new value of m20 + * @param m21 + * the new value of m21 + * @param m22 + * the new value of m22 + * @param m30 + * the new value of m30 + * @param m31 + * the new value of m31 + * @param m32 + * the new value of m32 + * @return this + */ + public Matrix4x3f set(float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22, + float m30, float m31, float m32) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + this.m30 = m30; + this.m31 = m31; + this.m32 = m32; + return determineProperties(); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 3, 6, 9
+ * 1, 4, 7, 10
+ * 2, 5, 8, 11
+ * + * @see #set(float[]) + * + * @param m + * the array to read the matrix values from + * @param off + * the offset into the array + * @return this + */ + public Matrix4x3f set(float m[], int off) { + MemUtil.INSTANCE.copy(m, off, this); + return determineProperties(); + } + + /** + * Set the values in the matrix using a float array that contains the matrix elements in column-major order. + *

+ * The results will look like this:

+ * + * 0, 3, 6, 9
+ * 1, 4, 7, 10
+ * 2, 5, 8, 11
+ * + * @see #set(float[], int) + * + * @param m + * the array to read the matrix values from + * @return this + */ + public Matrix4x3f set(float m[]) { + return set(m, 0); + } + + /** + * Set the values of this matrix by reading 12 float values from the given {@link FloatBuffer} in column-major order, + * starting at its current position. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3f set(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 float values from the given {@link ByteBuffer} in column-major order, + * starting at its current position. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3f set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 float values from the given {@link FloatBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The FloatBuffer is expected to contain the values in column-major order. + *

+ * The position of the FloatBuffer will not be changed by this method. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * the FloatBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3f set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return determineProperties(); + } + + /** + * Set the values of this matrix by reading 12 float values from the given {@link ByteBuffer} in column-major order, + * starting at the specified absolute buffer position/index. + *

+ * The ByteBuffer is expected to contain the values in column-major order. + *

+ * The position of the ByteBuffer will not be changed by this method. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * the ByteBuffer to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3f set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return determineProperties(); + } + /** + * Set the values of this matrix by reading 12 float values from off-heap memory in column-major order, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the matrix values from in column-major order + * @return this + */ + public Matrix4x3f setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return determineProperties(); + } + + public float determinant() { + return (m00 * m11 - m01 * m10) * m22 + + (m02 * m10 - m00 * m12) * m21 + + (m01 * m12 - m02 * m11) * m20; + } + + public Matrix4x3f invert(Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return invertOrthonormal(dest); + return invertGeneric(dest); + } + private Matrix4x3f invertGeneric(Matrix4x3f dest) { + float m11m00 = m00 * m11, m10m01 = m01 * m10, m10m02 = m02 * m10; + float m12m00 = m00 * m12, m12m01 = m01 * m12, m11m02 = m02 * m11; + float s = 1.0f / ((m11m00 - m10m01) * m22 + (m10m02 - m12m00) * m21 + (m12m01 - m11m02) * m20); + float m10m22 = m10 * m22, m10m21 = m10 * m21, m11m22 = m11 * m22; + float m11m20 = m11 * m20, m12m21 = m12 * m21, m12m20 = m12 * m20; + float m20m02 = m20 * m02, m20m01 = m20 * m01, m21m02 = m21 * m02; + float m21m00 = m21 * m00, m22m01 = m22 * m01, m22m00 = m22 * m00; + float nm00 = (m11m22 - m12m21) * s; + float nm01 = (m21m02 - m22m01) * s; + float nm02 = (m12m01 - m11m02) * s; + float nm10 = (m12m20 - m10m22) * s; + float nm11 = (m22m00 - m20m02) * s; + float nm12 = (m10m02 - m12m00) * s; + float nm20 = (m10m21 - m11m20) * s; + float nm21 = (m20m01 - m21m00) * s; + float nm22 = (m11m00 - m10m01) * s; + float nm30 = (m10m22 * m31 - m10m21 * m32 + m11m20 * m32 - m11m22 * m30 + m12m21 * m30 - m12m20 * m31) * s; + float nm31 = (m20m02 * m31 - m20m01 * m32 + m21m00 * m32 - m21m02 * m30 + m22m01 * m30 - m22m00 * m31) * s; + float nm32 = (m11m02 * m30 - m12m01 * m30 + m12m00 * m31 - m10m02 * m31 + m10m01 * m32 - m11m00 * m32) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = 0; + return dest; + } + private Matrix4x3f invertOrthonormal(Matrix4x3f dest) { + float nm30 = -(m00 * m30 + m01 * m31 + m02 * m32); + float nm31 = -(m10 * m30 + m11 * m31 + m12 * m32); + float nm32 = -(m20 * m30 + m21 * m31 + m22 * m32); + float m01 = this.m01; + float m02 = this.m02; + float m12 = this.m12; + dest.m00 = m00; + dest.m01 = m10; + dest.m02 = m20; + dest.m10 = m01; + dest.m11 = m11; + dest.m12 = m21; + dest.m20 = m02; + dest.m21 = m12; + dest.m22 = m22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = PROPERTY_ORTHONORMAL; + return dest; + } + + public Matrix4f invert(Matrix4f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return invertOrthonormal(dest); + return invertGeneric(dest); + } + private Matrix4f invertGeneric(Matrix4f dest) { + float m11m00 = m00 * m11, m10m01 = m01 * m10, m10m02 = m02 * m10; + float m12m00 = m00 * m12, m12m01 = m01 * m12, m11m02 = m02 * m11; + float s = 1.0f / ((m11m00 - m10m01) * m22 + (m10m02 - m12m00) * m21 + (m12m01 - m11m02) * m20); + float m10m22 = m10 * m22, m10m21 = m10 * m21, m11m22 = m11 * m22; + float m11m20 = m11 * m20, m12m21 = m12 * m21, m12m20 = m12 * m20; + float m20m02 = m20 * m02, m20m01 = m20 * m01, m21m02 = m21 * m02; + float m21m00 = m21 * m00, m22m01 = m22 * m01, m22m00 = m22 * m00; + float nm00 = (m11m22 - m12m21) * s; + float nm01 = (m21m02 - m22m01) * s; + float nm02 = (m12m01 - m11m02) * s; + float nm10 = (m12m20 - m10m22) * s; + float nm11 = (m22m00 - m20m02) * s; + float nm12 = (m10m02 - m12m00) * s; + float nm20 = (m10m21 - m11m20) * s; + float nm21 = (m20m01 - m21m00) * s; + float nm22 = (m11m00 - m10m01) * s; + float nm30 = (m10m22 * m31 - m10m21 * m32 + m11m20 * m32 - m11m22 * m30 + m12m21 * m30 - m12m20 * m31) * s; + float nm31 = (m20m02 * m31 - m20m01 * m32 + m21m00 * m32 - m21m02 * m30 + m22m01 * m30 - m22m00 * m31) * s; + float nm32 = (m11m02 * m30 - m12m01 * m30 + m12m00 * m31 - m10m02 * m31 + m10m01 * m32 - m11m00 * m32) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m03 = 0.0f; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m13 = 0.0f; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m23 = 0.0f; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.m33 = 0.0f; + dest.properties = 0; + return dest; + } + private Matrix4f invertOrthonormal(Matrix4f dest) { + float nm30 = -(m00 * m30 + m01 * m31 + m02 * m32); + float nm31 = -(m10 * m30 + m11 * m31 + m12 * m32); + float nm32 = -(m20 * m30 + m21 * m31 + m22 * m32); + float m01 = this.m01; + float m02 = this.m02; + float m12 = this.m12; + dest.m00 = m00; + dest.m01 = m10; + dest.m02 = m20; + dest.m03 = 0.0f; + dest.m10 = m01; + dest.m11 = m11; + dest.m12 = m21; + dest.m13 = 0.0f; + dest.m20 = m02; + dest.m21 = m12; + dest.m22 = m22; + dest.m23 = 0.0f; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.m33 = 0.0f; + dest.properties = PROPERTY_ORTHONORMAL; + return dest; + } + + /** + * Invert this matrix. + * + * @return this + */ + public Matrix4x3f invert() { + return invert(this); + } + + public Matrix4x3f invertOrtho(Matrix4x3f dest) { + float invM00 = 1.0f / m00; + float invM11 = 1.0f / m11; + float invM22 = 1.0f / m22; + dest.set(invM00, 0, 0, + 0, invM11, 0, + 0, 0, invM22, + -m30 * invM00, -m31 * invM11, -m32 * invM22); + dest.properties = 0; + return dest; + } + + /** + * Invert this orthographic projection matrix. + *

+ * This method can be used to quickly obtain the inverse of an orthographic projection matrix. + * + * @return this + */ + public Matrix4x3f invertOrtho() { + return invertOrtho(this); + } + + /** + * Transpose only the left 3x3 submatrix of this matrix and set the rest of the matrix elements to identity. + * + * @return this + */ + public Matrix4x3f transpose3x3() { + return transpose3x3(this); + } + + public Matrix4x3f transpose3x3(Matrix4x3f dest) { + float nm00 = m00; + float nm01 = m10; + float nm02 = m20; + float nm10 = m01; + float nm11 = m11; + float nm12 = m21; + float nm20 = m02; + float nm21 = m12; + float nm22 = m22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.properties = properties; + return dest; + } + + public Matrix3f transpose3x3(Matrix3f dest) { + dest.m00(m00); + dest.m01(m10); + dest.m02(m20); + dest.m10(m01); + dest.m11(m11); + dest.m12(m21); + dest.m20(m02); + dest.m21(m12); + dest.m22(m22); + return dest; + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + *

+ * In order to post-multiply a translation transformation directly to a + * matrix, use {@link #translate(float, float, float) translate()} instead. + * + * @see #translate(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4x3f translation(float x, float y, float z) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + m30 = x; + m31 = y; + m32 = z; + properties = PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to be a simple translation matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional translation. + *

+ * In order to post-multiply a translation transformation directly to a + * matrix, use {@link #translate(Vector3fc) translate()} instead. + * + * @see #translate(float, float, float) + * + * @param offset + * the offsets in x, y and z to translate + * @return this + */ + public Matrix4x3f translation(Vector3fc offset) { + return translation(offset.x(), offset.y(), offset.z()); + } + + /** + * Set only the translation components (m30, m31, m32) of this matrix to the given values (x, y, z). + *

+ * To build a translation matrix instead, use {@link #translation(float, float, float)}. + * To apply a translation, use {@link #translate(float, float, float)}. + * + * @see #translation(float, float, float) + * @see #translate(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4x3f setTranslation(float x, float y, float z) { + m30 = x; + m31 = y; + m32 = z; + properties &= ~(PROPERTY_IDENTITY); + return this; + } + + /** + * Set only the translation components (m30, m31, m32) of this matrix to the values (xyz.x, xyz.y, xyz.z). + *

+ * To build a translation matrix instead, use {@link #translation(Vector3fc)}. + * To apply a translation, use {@link #translate(Vector3fc)}. + * + * @see #translation(Vector3fc) + * @see #translate(Vector3fc) + * + * @param xyz + * the units to translate in (x, y, z) + * @return this + */ + public Matrix4x3f setTranslation(Vector3fc xyz) { + return setTranslation(xyz.x(), xyz.y(), xyz.z()); + } + + public Vector3f getTranslation(Vector3f dest) { + dest.x = m30; + dest.y = m31; + dest.z = m32; + return dest; + } + + public Vector3f getScale(Vector3f dest) { + dest.x = Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02); + dest.y = Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12); + dest.z = Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22); + return dest; + } + + /** + * Return a string representation of this matrix. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + String str = toString(Options.NUMBER_FORMAT); + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + /** + * Return a string representation of this matrix by formatting the matrix elements with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the matrix values with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return Runtime.format(m00, formatter) + " " + Runtime.format(m10, formatter) + " " + Runtime.format(m20, formatter) + " " + Runtime.format(m30, formatter) + "\n" + + Runtime.format(m01, formatter) + " " + Runtime.format(m11, formatter) + " " + Runtime.format(m21, formatter) + " " + Runtime.format(m31, formatter) + "\n" + + Runtime.format(m02, formatter) + " " + Runtime.format(m12, formatter) + " " + Runtime.format(m22, formatter) + " " + Runtime.format(m32, formatter) + "\n"; + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link #set(Matrix4x3fc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see #set(Matrix4x3fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix4x3f get(Matrix4x3f dest) { + return dest.set(this); + } + + /** + * Get the current values of this matrix and store them into + * dest. + *

+ * This is the reverse method of {@link Matrix4x3d#set(Matrix4x3fc)} and allows to obtain + * intermediate calculation results when chaining multiple transformations. + * + * @see Matrix4x3d#set(Matrix4x3fc) + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + public Matrix4x3d get(Matrix4x3d dest) { + return dest.set(this); + } + + public AxisAngle4f getRotation(AxisAngle4f dest) { + return dest.set(this); + } + + public AxisAngle4d getRotation(AxisAngle4d dest) { + return dest.set(this); + } + + public Quaternionf getUnnormalizedRotation(Quaternionf dest) { + return dest.setFromUnnormalized(this); + } + + public Quaternionf getNormalizedRotation(Quaternionf dest) { + return dest.setFromNormalized(this); + } + + public Quaterniond getUnnormalizedRotation(Quaterniond dest) { + return dest.setFromUnnormalized(this); + } + + public Quaterniond getNormalizedRotation(Quaterniond dest) { + return dest.setFromNormalized(this); + } + + + public FloatBuffer get(FloatBuffer buffer) { + return get(buffer.position(), buffer); + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + return get(buffer.position(), buffer); + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Matrix4x3fc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + public float[] get(float[] arr, int offset) { + MemUtil.INSTANCE.copy(this, arr, offset); + return arr; + } + + public float[] get(float[] arr) { + return get(arr, 0); + } + + public float[] get4x4(float[] arr, int offset) { + MemUtil.INSTANCE.copy4x4(this, arr, offset); + return arr; + } + + public float[] get4x4(float[] arr) { + return get4x4(arr, 0); + } + + public FloatBuffer get4x4(FloatBuffer buffer) { + return get4x4(buffer.position(), buffer); + } + + public FloatBuffer get4x4(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, index, buffer); + return buffer; + } + + public ByteBuffer get4x4(ByteBuffer buffer) { + return get4x4(buffer.position(), buffer); + } + + public ByteBuffer get4x4(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put4x4(this, index, buffer); + return buffer; + } + + public FloatBuffer get3x4(FloatBuffer buffer) { + return get3x4(buffer.position(), buffer); + } + + public FloatBuffer get3x4(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put3x4(this, index, buffer); + return buffer; + } + + public ByteBuffer get3x4(ByteBuffer buffer) { + return get3x4(buffer.position(), buffer); + } + + public ByteBuffer get3x4(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put3x4(this, index, buffer); + return buffer; + } + + public FloatBuffer getTransposed(FloatBuffer buffer) { + return getTransposed(buffer.position(), buffer); + } + + public FloatBuffer getTransposed(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public ByteBuffer getTransposed(ByteBuffer buffer) { + return getTransposed(buffer.position(), buffer); + } + + public ByteBuffer getTransposed(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putTransposed(this, index, buffer); + return buffer; + } + + public float[] getTransposed(float[] arr, int offset) { + arr[offset+0] = m00; + arr[offset+1] = m10; + arr[offset+2] = m20; + arr[offset+3] = m30; + arr[offset+4] = m01; + arr[offset+5] = m11; + arr[offset+6] = m21; + arr[offset+7] = m31; + arr[offset+8] = m02; + arr[offset+9] = m12; + arr[offset+10] = m22; + arr[offset+11] = m32; + return arr; + } + + public float[] getTransposed(float[] arr) { + return getTransposed(arr, 0); + } + + /** + * Set all the values within this matrix to 0. + * + * @return this + */ + public Matrix4x3f zero() { + MemUtil.INSTANCE.zero(this); + properties = 0; + return this; + } + + /** + * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(float) scale()} instead. + * + * @see #scale(float) + * + * @param factor + * the scale factor in x, y and z + * @return this + */ + public Matrix4x3f scaling(float factor) { + return scaling(factor, factor, factor); + } + + /** + * Set this matrix to be a simple scale matrix. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix, use {@link #scale(float, float, float) scale()} instead. + * + * @see #scale(float, float, float) + * + * @param x + * the scale in x + * @param y + * the scale in y + * @param z + * the scale in z + * @return this + */ + public Matrix4x3f scaling(float x, float y, float z) { + if ((properties & PROPERTY_IDENTITY) == 0) + MemUtil.INSTANCE.identity(this); + m00 = x; + m11 = y; + m22 = z; + boolean one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z); + properties = one ? PROPERTY_ORTHONORMAL : 0; + return this; + } + + /** + * Set this matrix to be a simple scale matrix which scales the base axes by xyz.x, xyz.y and xyz.z respectively. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional scaling. + *

+ * In order to post-multiply a scaling transformation directly to a + * matrix use {@link #scale(Vector3fc) scale()} instead. + * + * @see #scale(Vector3fc) + * + * @param xyz + * the scale in x, y and z respectively + * @return this + */ + public Matrix4x3f scaling(Vector3fc xyz) { + return scaling(xyz.x(), xyz.y(), xyz.z()); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to post-multiply a rotation transformation directly to a + * matrix, use {@link #rotate(float, Vector3fc) rotate()} instead. + * + * @see #rotate(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the axis to rotate about (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix4x3f rotation(float angle, Vector3fc axis) { + return rotation(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this matrix to a rotation transformation using the given {@link AxisAngle4f}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(AxisAngle4f) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix4x3f rotation(AxisAngle4f axisAngle) { + return rotation(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this matrix to a rotation matrix which rotates the given radians about a given axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(float, float, float, float) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * + * @param angle + * the angle in radians + * @param x + * the x-component of the rotation axis + * @param y + * the y-component of the rotation axis + * @param z + * the z-component of the rotation axis + * @return this + */ + public Matrix4x3f rotation(float angle, float x, float y, float z) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotationX(x * angle); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotationY(y * angle); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotationZ(z * angle); + return rotationInternal(angle, x, y, z); + } + private Matrix4x3f rotationInternal(float angle, float x, float y, float z) { + float sin = Math.sin(angle); + float cos = Math.cosFromSin(sin, angle); + float C = 1.0f - cos; + float xy = x * y, xz = x * z, yz = y * z; + m00 = cos + x * x * C; + m01 = xy * C + z * sin; + m02 = xz * C - y * sin; + m10 = xy * C - z * sin; + m11 = cos + y * y * C; + m12 = yz * C + x * sin; + m20 = xz * C + y * sin; + m21 = yz * C - x * sin; + m22 = cos + z * z * C; + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3f rotationX(float ang) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = 1.0f; + m01 = 0.0f; + m02 = 0.0f; + m10 = 0.0f; + m11 = cos; + m12 = sin; + m20 = 0.0f; + m21 = -sin; + m22 = cos; + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3f rotationY(float ang) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = cos; + m01 = 0.0f; + m02 = -sin; + m10 = 0.0f; + m11 = 1.0f; + m12 = 0.0f; + m20 = sin; + m21 = 0.0f; + m22 = cos; + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation transformation about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3f rotationZ(float ang) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + m00 = cos; + m01 = sin; + m02 = 0.0f; + m10 = -sin; + m11 = cos; + m12 = 0.0f; + m20 = 0.0f; + m21 = 0.0f; + m22 = 1.0f; + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3f rotationXYZ(float angleX, float angleY, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinX = -sinX; + float m_sinY = -sinY; + float m_sinZ = -sinZ; + + // rotateX + float nm11 = cosX; + float nm12 = sinX; + float nm21 = m_sinX; + float nm22 = cosX; + // rotateY + float nm00 = cosY; + float nm01 = nm21 * m_sinY; + float nm02 = nm22 * m_sinY; + m20 = sinY; + m21 = nm21 * cosY; + m22 = nm22 * cosY; + // rotateZ + m00 = nm00 * cosZ; + m01 = nm01 * cosZ + nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ; + m11 = nm01 * m_sinZ + nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + // set last column to identity + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4x3f rotationZYX(float angleZ, float angleY, float angleX) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinZ = -sinZ; + float m_sinY = -sinY; + float m_sinX = -sinX; + + // rotateZ + float nm00 = cosZ; + float nm01 = sinZ; + float nm10 = m_sinZ; + float nm11 = cosZ; + // rotateY + float nm20 = nm00 * sinY; + float nm21 = nm01 * sinY; + float nm22 = cosY; + m00 = nm00 * cosY; + m01 = nm01 * cosY; + m02 = m_sinY; + // rotateX + m10 = nm10 * cosX + nm20 * sinX; + m11 = nm11 * cosX + nm21 * sinX; + m12 = nm22 * sinX; + m20 = nm10 * m_sinX + nm20 * cosX; + m21 = nm11 * m_sinX + nm21 * cosX; + m22 = nm22 * cosX; + // set last column to identity + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: rotationY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3f rotationYXZ(float angleY, float angleX, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinY = -sinY; + float m_sinX = -sinX; + float m_sinZ = -sinZ; + + // rotateY + float nm00 = cosY; + float nm02 = m_sinY; + float nm20 = sinY; + float nm22 = cosY; + // rotateX + float nm10 = nm20 * sinX; + float nm11 = cosX; + float nm12 = nm22 * sinX; + m20 = nm20 * cosX; + m21 = m_sinX; + m22 = nm22 * cosX; + // rotateZ + m00 = nm00 * cosZ + nm10 * sinZ; + m01 = nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ + nm10 * cosZ; + m11 = nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + // set last column to identity + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set only the left 3x3 submatrix of this matrix to a rotation of angleX radians about the X axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3f setRotationXYZ(float angleX, float angleY, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinX = -sinX; + float m_sinY = -sinY; + float m_sinZ = -sinZ; + + // rotateX + float nm11 = cosX; + float nm12 = sinX; + float nm21 = m_sinX; + float nm22 = cosX; + // rotateY + float nm00 = cosY; + float nm01 = nm21 * m_sinY; + float nm02 = nm22 * m_sinY; + m20 = sinY; + m21 = nm21 * cosY; + m22 = nm22 * cosY; + // rotateZ + m00 = nm00 * cosZ; + m01 = nm01 * cosZ + nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ; + m11 = nm01 * m_sinZ + nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set only the left 3x3 submatrix of this matrix to a rotation of angleZ radians about the Z axis, followed by a rotation + * of angleY radians about the Y axis and followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4x3f setRotationZYX(float angleZ, float angleY, float angleX) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinZ = -sinZ; + float m_sinY = -sinY; + float m_sinX = -sinX; + + // rotateZ + float nm00 = cosZ; + float nm01 = sinZ; + float nm10 = m_sinZ; + float nm11 = cosZ; + // rotateY + float nm20 = nm00 * sinY; + float nm21 = nm01 * sinY; + float nm22 = cosY; + m00 = nm00 * cosY; + m01 = nm01 * cosY; + m02 = m_sinY; + // rotateX + m10 = nm10 * cosX + nm20 * sinX; + m11 = nm11 * cosX + nm21 * sinX; + m12 = nm22 * sinX; + m20 = nm10 * m_sinX + nm20 * cosX; + m21 = nm11 * m_sinX + nm21 * cosX; + m22 = nm22 * cosX; + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set only the left 3x3 submatrix of this matrix to a rotation of angleY radians about the Y axis, followed by a rotation + * of angleX radians about the X axis and followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3f setRotationYXZ(float angleY, float angleX, float angleZ) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinY = -sinY; + float m_sinX = -sinX; + float m_sinZ = -sinZ; + + // rotateY + float nm00 = cosY; + float nm02 = m_sinY; + float nm20 = sinY; + float nm22 = cosY; + // rotateX + float nm10 = nm20 * sinX; + float nm11 = cosX; + float nm12 = nm22 * sinX; + m20 = nm20 * cosX; + m21 = m_sinX; + m22 = nm22 * cosX; + // rotateZ + m00 = nm00 * cosZ + nm10 * sinZ; + m01 = nm11 * sinZ; + m02 = nm02 * cosZ + nm12 * sinZ; + m10 = nm00 * m_sinZ + nm10 * cosZ; + m11 = nm11 * cosZ; + m12 = nm02 * m_sinZ + nm12 * cosZ; + properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return this; + } + + /** + * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaternionfc}. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * The resulting matrix can be multiplied against another transformation + * matrix to obtain an additional rotation. + *

+ * In order to apply the rotation transformation to an existing transformation, + * use {@link #rotate(Quaternionfc) rotate()} instead. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4x3f rotation(Quaternionfc quat) { + float w2 = quat.w() * quat.w(); + float x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(); + float z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw; + float xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz; + float yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz; + float xw = quat.x() * quat.w(), dxw = xw + xw; + _m00(w2 + x2 - z2 - y2); + _m01(dxy + dzw); + _m02(dxz - dyw); + _m10(dxy - dzw); + _m11(y2 - z2 + w2 - x2); + _m12(dyz + dxw); + _m20(dyw + dxz); + _m21(dyz - dxw); + _m22(z2 - y2 - x2 + w2); + _m30(0.0f); + _m31(0.0f); + _m32(0.0f); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to T * R * S, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), and S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz). + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * @see #scale(float, float, float) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @return this + */ + public Matrix4x3f translationRotateScale(float tx, float ty, float tz, + float qx, float qy, float qz, float qw, + float sx, float sy, float sz) { + float dqx = qx + qx; + float dqy = qy + qy; + float dqz = qz + qz; + float q00 = dqx * qx; + float q11 = dqy * qy; + float q22 = dqz * qz; + float q01 = dqx * qy; + float q02 = dqx * qz; + float q03 = dqx * qw; + float q12 = dqy * qz; + float q13 = dqy * qw; + float q23 = dqz * qw; + m00 = sx - (q11 + q22) * sx; + m01 = (q01 + q23) * sx; + m02 = (q02 - q13) * sx; + m10 = (q01 - q23) * sy; + m11 = sy - (q22 + q00) * sy; + m12 = (q12 + q03) * sy; + m20 = (q02 + q13) * sz; + m21 = (q12 - q03) * sz; + m22 = sz - (q11 + q00) * sz; + m30 = tx; + m31 = ty; + m32 = tz; + properties = 0; + return this; + } + + /** + * Set this matrix to T * R * S, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, and S is a scaling transformation + * which scales the axes by scale. + *

+ * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaternionfc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @return this + */ + public Matrix4x3f translationRotateScale(Vector3fc translation, + Quaternionfc quat, + Vector3fc scale) { + return translationRotateScale(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z()); + } + + /** + * Set this matrix to T * R * S * M, where T is a translation by the given (tx, ty, tz), + * R is a rotation transformation specified by the quaternion (qx, qy, qz, qw), S is a scaling transformation + * which scales the three axes x, y and z by (sx, sy, sz). + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz).mul(m) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * @see #scale(float, float, float) + * @see #mul(Matrix4x3fc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param sx + * the scaling factor for the x-axis + * @param sy + * the scaling factor for the y-axis + * @param sz + * the scaling factor for the z-axis + * @param m + * the matrix to multiply by + * @return this + */ + public Matrix4x3f translationRotateScaleMul(float tx, float ty, float tz, + float qx, float qy, float qz, float qw, + float sx, float sy, float sz, + Matrix4x3f m) { + float dqx = qx + qx; + float dqy = qy + qy; + float dqz = qz + qz; + float q00 = dqx * qx; + float q11 = dqy * qy; + float q22 = dqz * qz; + float q01 = dqx * qy; + float q02 = dqx * qz; + float q03 = dqx * qw; + float q12 = dqy * qz; + float q13 = dqy * qw; + float q23 = dqz * qw; + float nm00 = sx - (q11 + q22) * sx; + float nm01 = (q01 + q23) * sx; + float nm02 = (q02 - q13) * sx; + float nm10 = (q01 - q23) * sy; + float nm11 = sy - (q22 + q00) * sy; + float nm12 = (q12 + q03) * sy; + float nm20 = (q02 + q13) * sz; + float nm21 = (q12 - q03) * sz; + float nm22 = sz - (q11 + q00) * sz; + float m00 = nm00 * m.m00 + nm10 * m.m01 + nm20 * m.m02; + float m01 = nm01 * m.m00 + nm11 * m.m01 + nm21 * m.m02; + m02 = nm02 * m.m00 + nm12 * m.m01 + nm22 * m.m02; + this.m00 = m00; + this.m01 = m01; + float m10 = nm00 * m.m10 + nm10 * m.m11 + nm20 * m.m12; + float m11 = nm01 * m.m10 + nm11 * m.m11 + nm21 * m.m12; + m12 = nm02 * m.m10 + nm12 * m.m11 + nm22 * m.m12; + this.m10 = m10; + this.m11 = m11; + float m20 = nm00 * m.m20 + nm10 * m.m21 + nm20 * m.m22; + float m21 = nm01 * m.m20 + nm11 * m.m21 + nm21 * m.m22; + m22 = nm02 * m.m20 + nm12 * m.m21 + nm22 * m.m22; + this.m20 = m20; + this.m21 = m21; + float m30 = nm00 * m.m30 + nm10 * m.m31 + nm20 * m.m32 + tx; + float m31 = nm01 * m.m30 + nm11 * m.m31 + nm21 * m.m32 + ty; + m32 = nm02 * m.m30 + nm12 * m.m31 + nm22 * m.m32 + tz; + this.m30 = m30; + this.m31 = m31; + properties = 0; + return this; + } + + /** + * Set this matrix to T * R * S * M, where T is the given translation, + * R is a rotation transformation specified by the given quaternion, S is a scaling transformation + * which scales the axes by scale. + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(translation).rotate(quat).scale(scale).mul(m) + * + * @see #translation(Vector3fc) + * @see #rotate(Quaternionfc) + * + * @param translation + * the translation + * @param quat + * the quaternion representing a rotation + * @param scale + * the scaling factors + * @param m + * the matrix to multiply by + * @return this + */ + public Matrix4x3f translationRotateScaleMul(Vector3fc translation, Quaternionfc quat, Vector3fc scale, Matrix4x3f m) { + return translationRotateScaleMul(translation.x(), translation.y(), translation.z(), quat.x(), quat.y(), quat.z(), quat.w(), scale.x(), scale.y(), scale.z(), m); + } + + /** + * Set this matrix to T * R, where T is a translation by the given (tx, ty, tz) and + * R is a rotation transformation specified by the given quaternion. + *

+ * When transforming a vector by the resulting matrix the rotation transformation will be applied first and then the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param quat + * the quaternion representing a rotation + * @return this + */ + public Matrix4x3f translationRotate(float tx, float ty, float tz, Quaternionfc quat) { + float dqx = quat.x() + quat.x(); + float dqy = quat.y() + quat.y(); + float dqz = quat.z() + quat.z(); + float q00 = dqx * quat.x(); + float q11 = dqy * quat.y(); + float q22 = dqz * quat.z(); + float q01 = dqx * quat.y(); + float q02 = dqx * quat.z(); + float q03 = dqx * quat.w(); + float q12 = dqy * quat.z(); + float q13 = dqy * quat.w(); + float q23 = dqz * quat.w(); + m00 = 1.0f - (q11 + q22); + m01 = q01 + q23; + m02 = q02 - q13; + m10 = q01 - q23; + m11 = 1.0f - (q22 + q00); + m12 = q12 + q03; + m20 = q02 + q13; + m21 = q12 - q03; + m22 = 1.0f - (q11 + q00); + m30 = tx; + m31 = ty; + m32 = tz; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to T * R * M, where T is a translation by the given (tx, ty, tz), + * R is a rotation - and possibly scaling - transformation specified by the given quaternion and M is the given matrix mat. + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).mul(mat) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * @see #mul(Matrix4x3fc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param quat + * the quaternion representing a rotation + * @param mat + * the matrix to multiply with + * @return this + */ + public Matrix4x3f translationRotateMul(float tx, float ty, float tz, Quaternionfc quat, Matrix4x3fc mat) { + return translationRotateMul(tx, ty, tz, quat.x(), quat.y(), quat.z(), quat.w(), mat); + } + + /** + * Set this matrix to T * R * M, where T is a translation by the given (tx, ty, tz), + * R is a rotation - and possibly scaling - transformation specified by the quaternion (qx, qy, qz, qw) and M is the given matrix mat + *

+ * When transforming a vector by the resulting matrix the transformation described by M will be applied first, then the scaling, then rotation and + * at last the translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(tx, ty, tz).rotate(quat).mul(mat) + * + * @see #translation(float, float, float) + * @see #rotate(Quaternionfc) + * @see #mul(Matrix4x3fc) + * + * @param tx + * the number of units by which to translate the x-component + * @param ty + * the number of units by which to translate the y-component + * @param tz + * the number of units by which to translate the z-component + * @param qx + * the x-coordinate of the vector part of the quaternion + * @param qy + * the y-coordinate of the vector part of the quaternion + * @param qz + * the z-coordinate of the vector part of the quaternion + * @param qw + * the scalar part of the quaternion + * @param mat + * the matrix to multiply with + * @return this + */ + public Matrix4x3f translationRotateMul(float tx, float ty, float tz, float qx, float qy, float qz, float qw, Matrix4x3fc mat) { + float w2 = qw * qw; + float x2 = qx * qx; + float y2 = qy * qy; + float z2 = qz * qz; + float zw = qz * qw; + float xy = qx * qy; + float xz = qx * qz; + float yw = qy * qw; + float yz = qy * qz; + float xw = qx * qw; + float nm00 = w2 + x2 - z2 - y2; + float nm01 = xy + zw + zw + xy; + float nm02 = xz - yw + xz - yw; + float nm10 = -zw + xy - zw + xy; + float nm11 = y2 - z2 + w2 - x2; + float nm12 = yz + yz + xw + xw; + float nm20 = yw + xz + xz + yw; + float nm21 = yz + yz - xw - xw; + float nm22 = z2 - y2 - x2 + w2; + m00 = nm00 * mat.m00() + nm10 * mat.m01() + nm20 * mat.m02(); + m01 = nm01 * mat.m00() + nm11 * mat.m01() + nm21 * mat.m02(); + m02 = nm02 * mat.m00() + nm12 * mat.m01() + nm22 * mat.m02(); + m10 = nm00 * mat.m10() + nm10 * mat.m11() + nm20 * mat.m12(); + m11 = nm01 * mat.m10() + nm11 * mat.m11() + nm21 * mat.m12(); + m12 = nm02 * mat.m10() + nm12 * mat.m11() + nm22 * mat.m12(); + m20 = nm00 * mat.m20() + nm10 * mat.m21() + nm20 * mat.m22(); + m21 = nm01 * mat.m20() + nm11 * mat.m21() + nm21 * mat.m22(); + m22 = nm02 * mat.m20() + nm12 * mat.m21() + nm22 * mat.m22(); + m30 = nm00 * mat.m30() + nm10 * mat.m31() + nm20 * mat.m32() + tx; + m31 = nm01 * mat.m30() + nm11 * mat.m31() + nm21 * mat.m32() + ty; + m32 = nm02 * mat.m30() + nm12 * mat.m31() + nm22 * mat.m32() + tz; + this.properties = 0; + return this; + } + + /** + * Set the left 3x3 submatrix of this {@link Matrix4x3f} to the given {@link Matrix3fc} and don't change the other elements. + * + * @param mat + * the 3x3 matrix + * @return this + */ + public Matrix4x3f set3x3(Matrix3fc mat) { + if (mat instanceof Matrix3f) { + MemUtil.INSTANCE.copy3x3((Matrix3f) mat, this); + } else { + set3x3Matrix3fc(mat); + } + properties = 0; + return this; + } + private void set3x3Matrix3fc(Matrix3fc mat) { + m00 = mat.m00(); + m01 = mat.m01(); + m02 = mat.m02(); + m10 = mat.m10(); + m11 = mat.m11(); + m12 = mat.m12(); + m20 = mat.m20(); + m21 = mat.m21(); + m22 = mat.m22(); + } + + public Vector4f transform(Vector4f v) { + return v.mul(this); + } + + public Vector4f transform(Vector4fc v, Vector4f dest) { + return v.mul(this, dest); + } + + public Vector3f transformPosition(Vector3f v) { + v.set(m00 * v.x + m10 * v.y + m20 * v.z + m30, + m01 * v.x + m11 * v.y + m21 * v.z + m31, + m02 * v.x + m12 * v.y + m22 * v.z + m32); + return v; + } + + public Vector3f transformPosition(Vector3fc v, Vector3f dest) { + dest.set(m00 * v.x() + m10 * v.y() + m20 * v.z() + m30, + m01 * v.x() + m11 * v.y() + m21 * v.z() + m31, + m02 * v.x() + m12 * v.y() + m22 * v.z() + m32); + return dest; + } + + public Vector3f transformDirection(Vector3f v) { + v.set(m00 * v.x + m10 * v.y + m20 * v.z, + m01 * v.x + m11 * v.y + m21 * v.z, + m02 * v.x + m12 * v.y + m22 * v.z); + return v; + } + + public Vector3f transformDirection(Vector3fc v, Vector3f dest) { + dest.set(m00 * v.x() + m10 * v.y() + m20 * v.z(), + m01 * v.x() + m11 * v.y() + m21 * v.z(), + m02 * v.x() + m12 * v.y() + m22 * v.z()); + return dest; + } + + public Matrix4x3f scale(Vector3fc xyz, Matrix4x3f dest) { + return scale(xyz.x(), xyz.y(), xyz.z(), dest); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @return this + */ + public Matrix4x3f scale(Vector3fc xyz) { + return scale(xyz.x(), xyz.y(), xyz.z(), this); + } + + public Matrix4x3f scale(float xyz, Matrix4x3f dest) { + return scale(xyz, xyz, xyz, dest); + } + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * Individual scaling of all three axes can be applied using {@link #scale(float, float, float)}. + * + * @see #scale(float, float, float) + * + * @param xyz + * the factor for all components + * @return this + */ + public Matrix4x3f scale(float xyz) { + return scale(xyz, xyz, xyz); + } + + public Matrix4x3f scaleXY(float x, float y, Matrix4x3f dest) { + return scale(x, y, 1.0f, dest); + } + + /** + * Apply scaling to this matrix by scaling the X axis by x and the Y axis by y. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @return this + */ + public Matrix4x3f scaleXY(float x, float y) { + return scale(x, y, 1.0f); + } + + public Matrix4x3f scale(float x, float y, float z, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.scaling(x, y, z); + return scaleGeneric(x, y, z, dest); + } + private Matrix4x3f scaleGeneric(float x, float y, float z, Matrix4x3f dest) { + dest.m00 = m00 * x; + dest.m01 = m01 * x; + dest.m02 = m02 * x; + dest.m10 = m10 * y; + dest.m11 = m11 * y; + dest.m12 = m12 * y; + dest.m20 = m20 * z; + dest.m21 = m21 * z; + dest.m22 = m22 * z; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return dest; + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix4x3f scale(float x, float y, float z) { + return scale(x, y, z, this); + } + + public Matrix4x3f scaleLocal(float x, float y, float z, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.scaling(x, y, z); + float nm00 = x * m00; + float nm01 = y * m01; + float nm02 = z * m02; + float nm10 = x * m10; + float nm11 = y * m11; + float nm12 = z * m12; + float nm20 = x * m20; + float nm21 = y * m21; + float nm22 = z * m22; + float nm30 = x * m30; + float nm31 = y * m31; + float nm32 = z * m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + return dest; + } + + public Matrix4x3f scaleAround(float sx, float sy, float sz, float ox, float oy, float oz, Matrix4x3f dest) { + float nm30 = m00 * ox + m10 * oy + m20 * oz + m30; + float nm31 = m01 * ox + m11 * oy + m21 * oz + m31; + float nm32 = m02 * ox + m12 * oy + m22 * oz + m32; + boolean one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz); + return dest + ._m00(m00 * sx) + ._m01(m01 * sx) + ._m02(m02 * sx) + ._m10(m10 * sy) + ._m11(m11 * sy) + ._m12(m12 * sy) + ._m20(m20 * sz) + ._m21(m21 * sz) + ._m22(m22 * sz) + ._m30(-dest.m00 * ox - dest.m10 * oy - dest.m20 * oz + nm30) + ._m31(-dest.m01 * ox - dest.m11 * oy - dest.m21 * oz + nm31) + ._m32(-dest.m02 * ox - dest.m12 * oy - dest.m22 * oz + nm32) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | (one ? 0 : PROPERTY_ORTHONORMAL))); + } + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4x3f scaleAround(float sx, float sy, float sz, float ox, float oy, float oz) { + return scaleAround(sx, sy, sz, ox, oy, oz, this); + } + + /** + * Apply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @return this + */ + public Matrix4x3f scaleAround(float factor, float ox, float oy, float oz) { + return scaleAround(factor, factor, factor, ox, oy, oz, this); + } + + public Matrix4x3f scaleAround(float factor, float ox, float oy, float oz, Matrix4x3f dest) { + return scaleAround(factor, factor, factor, ox, oy, oz, dest); + } + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v, the + * scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @return this + */ + public Matrix4x3f scaleLocal(float x, float y, float z) { + return scaleLocal(x, y, z, this); + } + + public Matrix4x3f rotateX(float ang, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationX(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float x = m30, y = m31, z = m32; + return dest.rotationX(ang).setTranslation(x, y, z); + } + return rotateXInternal(ang, dest); + } + private Matrix4x3f rotateXInternal(float ang, Matrix4x3f dest) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + float rm11 = cos; + float rm12 = sin; + float rm21 = -sin; + float rm22 = cos; + + // add temporaries for dependent values + float nm10 = m10 * rm11 + m20 * rm12; + float nm11 = m11 * rm11 + m21 * rm12; + float nm12 = m12 * rm11 + m22 * rm12; + // set non-dependent values directly + dest.m20 = m10 * rm21 + m20 * rm22; + dest.m21 = m11 * rm21 + m21 * rm22; + dest.m22 = m12 * rm21 + m22 * rm22; + // set other values + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3f rotateX(float ang) { + return rotateX(ang, this); + } + + public Matrix4x3f rotateY(float ang, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationY(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float x = m30, y = m31, z = m32; + return dest.rotationY(ang).setTranslation(x, y, z); + } + return rotateYInternal(ang, dest); + } + private Matrix4x3f rotateYInternal(float ang, Matrix4x3f dest) { + float cos, sin; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + float rm00 = cos; + float rm02 = -sin; + float rm20 = sin; + float rm22 = cos; + + // add temporaries for dependent values + float nm00 = m00 * rm00 + m20 * rm02; + float nm01 = m01 * rm00 + m21 * rm02; + float nm02 = m02 * rm00 + m22 * rm02; + // set non-dependent values directly + dest.m20 = m00 * rm20 + m20 * rm22; + dest.m21 = m01 * rm20 + m21 * rm22; + dest.m22 = m02 * rm20 + m22 * rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3f rotateY(float ang) { + return rotateY(ang, this); + } + + public Matrix4x3f rotateZ(float ang, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationZ(ang); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float x = m30, y = m31, z = m32; + return dest.rotationZ(ang).setTranslation(x, y, z); + } + return rotateZInternal(ang, dest); + } + private Matrix4x3f rotateZInternal(float ang, Matrix4x3f dest) { + float sin, cos; + sin = Math.sin(ang); + cos = Math.cosFromSin(sin, ang); + float rm00 = cos; + float rm01 = sin; + float rm10 = -sin; + float rm11 = cos; + + // add temporaries for dependent values + float nm00 = m00 * rm00 + m10 * rm01; + float nm01 = m01 * rm00 + m11 * rm01; + float nm02 = m02 * rm00 + m12 * rm01; + // set non-dependent values directly + dest.m10 = m00 * rm10 + m10 * rm11; + dest.m11 = m01 * rm10 + m11 * rm11; + dest.m12 = m02 * rm10 + m12 * rm11; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @return this + */ + public Matrix4x3f rotateZ(float ang) { + return rotateZ(ang, this); + } + + /** + * Apply rotation of angles.x radians about the X axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angles.x).rotateY(angles.y).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4x3f rotateXYZ(Vector3f angles) { + return rotateXYZ(angles.x, angles.y, angles.z); + } + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3f rotateXYZ(float angleX, float angleY, float angleZ) { + return rotateXYZ(angleX, angleY, angleZ, this); + } + + public Matrix4x3f rotateXYZ(float angleX, float angleY, float angleZ, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationXYZ(angleX, angleY, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float tx = m30, ty = m31, tz = m32; + return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz); + } + return rotateXYZInternal(angleX, angleY, angleZ, dest); + } + private Matrix4x3f rotateXYZInternal(float angleX, float angleY, float angleZ, Matrix4x3f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinX = -sinX; + float m_sinY = -sinY; + float m_sinZ = -sinZ; + + // rotateX + float nm10 = m10 * cosX + m20 * sinX; + float nm11 = m11 * cosX + m21 * sinX; + float nm12 = m12 * cosX + m22 * sinX; + float nm20 = m10 * m_sinX + m20 * cosX; + float nm21 = m11 * m_sinX + m21 * cosX; + float nm22 = m12 * m_sinX + m22 * cosX; + // rotateY + float nm00 = m00 * cosY + nm20 * m_sinY; + float nm01 = m01 * cosY + nm21 * m_sinY; + float nm02 = m02 * cosY + nm22 * m_sinY; + dest.m20 = m00 * sinY + nm20 * cosY; + dest.m21 = m01 * sinY + nm21 * cosY; + dest.m22 = m02 * sinY + nm22 * cosY; + // rotateZ + dest.m00 = nm00 * cosZ + nm10 * sinZ; + dest.m01 = nm01 * cosZ + nm11 * sinZ; + dest.m02 = nm02 * cosZ + nm12 * sinZ; + dest.m10 = nm00 * m_sinZ + nm10 * cosZ; + dest.m11 = nm01 * m_sinZ + nm11 * cosZ; + dest.m12 = nm02 * m_sinZ + nm12 * cosZ; + // copy last column from 'this' + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation of angles.z radians about the Z axis, followed by a rotation of angles.y radians about the Y axis and + * followed by a rotation of angles.x radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angles.z).rotateY(angles.y).rotateX(angles.x) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4x3f rotateZYX(Vector3f angles) { + return rotateZYX(angles.z, angles.y, angles.x); + } + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @return this + */ + public Matrix4x3f rotateZYX(float angleZ, float angleY, float angleX) { + return rotateZYX(angleZ, angleY, angleX, this); + } + + public Matrix4x3f rotateZYX(float angleZ, float angleY, float angleX, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationZYX(angleZ, angleY, angleX); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float tx = m30, ty = m31, tz = m32; + return dest.rotationZYX(angleZ, angleY, angleX).setTranslation(tx, ty, tz); + } + return rotateZYXInternal(angleZ, angleY, angleX, dest); + } + private Matrix4x3f rotateZYXInternal(float angleZ, float angleY, float angleX, Matrix4x3f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinZ = -sinZ; + float m_sinY = -sinY; + float m_sinX = -sinX; + + // rotateZ + float nm00 = m00 * cosZ + m10 * sinZ; + float nm01 = m01 * cosZ + m11 * sinZ; + float nm02 = m02 * cosZ + m12 * sinZ; + float nm10 = m00 * m_sinZ + m10 * cosZ; + float nm11 = m01 * m_sinZ + m11 * cosZ; + float nm12 = m02 * m_sinZ + m12 * cosZ; + // rotateY + float nm20 = nm00 * sinY + m20 * cosY; + float nm21 = nm01 * sinY + m21 * cosY; + float nm22 = nm02 * sinY + m22 * cosY; + dest.m00 = nm00 * cosY + m20 * m_sinY; + dest.m01 = nm01 * cosY + m21 * m_sinY; + dest.m02 = nm02 * cosY + m22 * m_sinY; + // rotateX + dest.m10 = nm10 * cosX + nm20 * sinX; + dest.m11 = nm11 * cosX + nm21 * sinX; + dest.m12 = nm12 * cosX + nm22 * sinX; + dest.m20 = nm10 * m_sinX + nm20 * cosX; + dest.m21 = nm11 * m_sinX + nm21 * cosX; + dest.m22 = nm12 * m_sinX + nm22 * cosX; + // copy last column from 'this' + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation of angles.y radians about the Y axis, followed by a rotation of angles.x radians about the X axis and + * followed by a rotation of angles.z radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z) + * + * @param angles + * the Euler angles + * @return this + */ + public Matrix4x3f rotateYXZ(Vector3f angles) { + return rotateYXZ(angles.y, angles.x, angles.z); + } + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @return this + */ + public Matrix4x3f rotateYXZ(float angleY, float angleX, float angleZ) { + return rotateYXZ(angleY, angleX, angleZ, this); + } + + public Matrix4x3f rotateYXZ(float angleY, float angleX, float angleZ, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotationYXZ(angleY, angleX, angleZ); + else if ((properties & PROPERTY_TRANSLATION) != 0) { + float tx = m30, ty = m31, tz = m32; + return dest.rotationYXZ(angleY, angleX, angleZ).setTranslation(tx, ty, tz); + } + return rotateYXZInternal(angleY, angleX, angleZ, dest); + } + private Matrix4x3f rotateYXZInternal(float angleY, float angleX, float angleZ, Matrix4x3f dest) { + float sinX = Math.sin(angleX); + float cosX = Math.cosFromSin(sinX, angleX); + float sinY = Math.sin(angleY); + float cosY = Math.cosFromSin(sinY, angleY); + float sinZ = Math.sin(angleZ); + float cosZ = Math.cosFromSin(sinZ, angleZ); + float m_sinY = -sinY; + float m_sinX = -sinX; + float m_sinZ = -sinZ; + + // rotateY + float nm20 = m00 * sinY + m20 * cosY; + float nm21 = m01 * sinY + m21 * cosY; + float nm22 = m02 * sinY + m22 * cosY; + float nm00 = m00 * cosY + m20 * m_sinY; + float nm01 = m01 * cosY + m21 * m_sinY; + float nm02 = m02 * cosY + m22 * m_sinY; + // rotateX + float nm10 = m10 * cosX + nm20 * sinX; + float nm11 = m11 * cosX + nm21 * sinX; + float nm12 = m12 * cosX + nm22 * sinX; + dest.m20 = m10 * m_sinX + nm20 * cosX; + dest.m21 = m11 * m_sinX + nm21 * cosX; + dest.m22 = m12 * m_sinX + nm22 * cosX; + // rotateZ + dest.m00 = nm00 * cosZ + nm10 * sinZ; + dest.m01 = nm01 * cosZ + nm11 * sinZ; + dest.m02 = nm02 * cosZ + nm12 * sinZ; + dest.m10 = nm00 * m_sinZ + nm10 * cosZ; + dest.m11 = nm01 * m_sinZ + nm11 * cosZ; + dest.m12 = nm02 * m_sinZ + nm12 * cosZ; + // copy last column from 'this' + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotate(float ang, float x, float y, float z, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(ang, x, y, z); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(ang, x, y, z, dest); + return rotateGeneric(ang, x, y, z, dest); + } + private Matrix4x3f rotateGeneric(float ang, float x, float y, float z, Matrix4x3f dest) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotateX(x * ang, dest); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotateY(y * ang, dest); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotateZ(z * ang, dest); + return rotateGenericInternal(ang, x, y, z, dest); + } + private Matrix4x3f rotateGenericInternal(float ang, float x, float y, float z, Matrix4x3f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float rm00 = xx * C + c; + float rm01 = xy * C + z * s; + float rm02 = xz * C - y * s; + float rm10 = xy * C - z * s; + float rm11 = yy * C + c; + float rm12 = yz * C + x * s; + float rm20 = xz * C + y * s; + float rm21 = yz * C - x * s; + float rm22 = zz * C + c; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4x3f rotate(float ang, float x, float y, float z) { + return rotate(ang, x, y, z, this); + } + + /** + * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * In order to set the matrix to a rotation matrix without post-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateTranslation(float ang, float x, float y, float z, Matrix4x3f dest) { + float tx = m30, ty = m31, tz = m32; + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return dest.rotationX(x * ang).setTranslation(tx, ty, tz); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return dest.rotationY(y * ang).setTranslation(tx, ty, tz); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return dest.rotationZ(z * ang).setTranslation(tx, ty, tz); + return rotateTranslationInternal(ang, x, y, z, dest); + } + private Matrix4x3f rotateTranslationInternal(float ang, float x, float y, float z, Matrix4x3f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float rm00 = xx * C + c; + float rm01 = xy * C + z * s; + float rm02 = xz * C - y * s; + float rm10 = xy * C - z * s; + float rm11 = yy * C + c; + float rm12 = yz * C + x * s; + float rm20 = xz * C + y * s; + float rm21 = yz * C - x * s; + float rm22 = zz * C + c; + float nm00 = rm00; + float nm01 = rm01; + float nm02 = rm02; + float nm10 = rm10; + float nm11 = rm11; + float nm12 = rm12; + // set non-dependent values directly + dest.m20 = rm20; + dest.m21 = rm21; + dest.m22 = rm22; + // set other values + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply the rotation transformation of the given {@link Quaternionfc} to this matrix while using (ox, oy, oz) as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4x3f rotateAround(Quaternionfc quat, float ox, float oy, float oz) { + return rotateAround(quat, ox, oy, oz, this); + } + + private Matrix4x3f rotateAroundAffine(Quaternionfc quat, float ox, float oy, float oz, Matrix4x3f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = dxy - dzw; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float tm30 = m00 * ox + m10 * oy + m20 * oz + m30; + float tm31 = m01 * ox + m11 * oy + m21 * oz + m31; + float tm32 = m02 * ox + m12 * oy + m22 * oz + m32; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest + ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22) + ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22) + ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22) + ._m00(nm00) + ._m01(nm01) + ._m02(nm02) + ._m10(nm10) + ._m11(nm11) + ._m12(nm12) + ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30) + ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31) + ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + public Matrix4x3f rotateAround(Quaternionfc quat, float ox, float oy, float oz, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return rotationAround(quat, ox, oy, oz); + return rotateAroundAffine(quat, ox, oy, oz, dest); + } + + /** + * Set this matrix to a transformation composed of a rotation of the specified {@link Quaternionfc} while using (ox, oy, oz) as the rotation origin. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * This method is equivalent to calling: translation(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @return this + */ + public Matrix4x3f rotationAround(Quaternionfc quat, float ox, float oy, float oz) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + this._m20(dyw + dxz); + this._m21(dyz - dxw); + this._m22(z2 - y2 - x2 + w2); + this._m00(w2 + x2 - z2 - y2); + this._m01(dxy + dzw); + this._m02(dxz - dyw); + this._m10(dxy - dzw); + this._m11(y2 - z2 + w2 - x2); + this._m12(dyz + dxw); + this._m30(-m00 * ox - m10 * oy - m20 * oz + ox); + this._m31(-m01 * ox - m11 * oy - m21 * oz + oy); + this._m32(-m02 * ox - m12 * oy - m22 * oz + oz); + this.properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateLocal(float ang, float x, float y, float z, Matrix4x3f dest) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotateLocalX(x * ang, dest); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotateLocalY(y * ang, dest); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotateLocalZ(z * ang, dest); + return rotateLocalInternal(ang, x, y, z, dest); + } + private Matrix4x3f rotateLocalInternal(float ang, float x, float y, float z, Matrix4x3f dest) { + float s = Math.sin(ang); + float c = Math.cosFromSin(s, ang); + float C = 1.0f - c; + float xx = x * x, xy = x * y, xz = x * z; + float yy = y * y, yz = y * z; + float zz = z * z; + float lm00 = xx * C + c; + float lm01 = xy * C + z * s; + float lm02 = xz * C - y * s; + float lm10 = xy * C - z * s; + float lm11 = yy * C + c; + float lm12 = yz * C + x * s; + float lm20 = xz * C + y * s; + float lm21 = yz * C - x * s; + float lm22 = zz * C + c; + float nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + float nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + float nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + float nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + float nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + float nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + float nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + float nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + float nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + float nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + float nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + float nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotation(float, float, float, float) rotation()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(float, float, float, float) + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @return this + */ + public Matrix4x3f rotateLocal(float ang, float x, float y, float z) { + return rotateLocal(ang, x, y, z, this); + } + + /** + * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians + * about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(float) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(float) + * + * @param ang + * the angle in radians to rotate about the X axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateLocalX(float ang, Matrix4x3f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm01 = cos * m01 - sin * m02; + float nm02 = sin * m01 + cos * m02; + float nm11 = cos * m11 - sin * m12; + float nm12 = sin * m11 + cos * m12; + float nm21 = cos * m21 - sin * m22; + float nm22 = sin * m21 + cos * m22; + float nm31 = cos * m31 - sin * m32; + float nm32 = sin * m31 + cos * m32; + dest + ._m00(m00) + ._m01(nm01) + ._m02(nm02) + ._m10(m10) + ._m11(nm11) + ._m12(nm12) + ._m20(m20) + ._m21(nm21) + ._m22(nm22) + ._m30(m30) + ._m31(nm31) + ._m32(nm32) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationX(float) rotationX()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationX(float) + * + * @param ang + * the angle in radians to rotate about the X axis + * @return this + */ + public Matrix4x3f rotateLocalX(float ang) { + return rotateLocalX(ang, this); + } + + /** + * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians + * about the Y axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateLocalY(float ang, Matrix4x3f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm00 = cos * m00 + sin * m02; + float nm02 = -sin * m00 + cos * m02; + float nm10 = cos * m10 + sin * m12; + float nm12 = -sin * m10 + cos * m12; + float nm20 = cos * m20 + sin * m22; + float nm22 = -sin * m20 + cos * m22; + float nm30 = cos * m30 + sin * m32; + float nm32 = -sin * m30 + cos * m32; + dest + ._m00(nm00) + ._m01(m01) + ._m02(nm02) + ._m10(nm10) + ._m11(m11) + ._m12(nm12) + ._m20(nm20) + ._m21(m21) + ._m22(nm22) + ._m30(nm30) + ._m31(m31) + ._m32(nm32) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationY(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Y axis + * @return this + */ + public Matrix4x3f rotateLocalY(float ang) { + return rotateLocalY(ang, this); + } + + /** + * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians + * about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(float) rotationZ()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationZ(float) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateLocalZ(float ang, Matrix4x3f dest) { + float sin = Math.sin(ang); + float cos = Math.cosFromSin(sin, ang); + float nm00 = cos * m00 - sin * m01; + float nm01 = sin * m00 + cos * m01; + float nm10 = cos * m10 - sin * m11; + float nm11 = sin * m10 + cos * m11; + float nm20 = cos * m20 - sin * m21; + float nm21 = sin * m20 + cos * m21; + float nm30 = cos * m30 - sin * m31; + float nm31 = sin * m30 + cos * m31; + dest + ._m00(nm00) + ._m01(nm01) + ._m02(m02) + ._m10(nm10) + ._m11(nm11) + ._m12(m12) + ._m20(nm20) + ._m21(nm21) + ._m22(m22) + ._m30(nm30) + ._m31(nm31) + ._m32(m32) + ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION)); + return dest; + } + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * In order to set the matrix to a rotation matrix without pre-multiplying the rotation + * transformation, use {@link #rotationZ(float) rotationY()}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotationY(float) + * + * @param ang + * the angle in radians to rotate about the Z axis + * @return this + */ + public Matrix4x3f rotateLocalZ(float ang) { + return rotateLocalZ(ang, this); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4x3f translate(Vector3fc offset) { + return translate(offset.x(), offset.y(), offset.z()); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f translate(Vector3fc offset, Matrix4x3f dest) { + return translate(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(float, float, float)}. + * + * @see #translation(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f translate(float x, float y, float z, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.translation(x, y, z); + return translateGeneric(x, y, z, dest); + } + private Matrix4x3f translateGeneric(float x, float y, float z, Matrix4x3f dest) { + MemUtil.INSTANCE.copy(this, dest); + dest.m30 = m00 * x + m10 * y + m20 * z + m30; + dest.m31 = m01 * x + m11 * y + m21 * z + m31; + dest.m32 = m02 * x + m12 * y + m22 * z + m32; + dest.properties = properties & ~(PROPERTY_IDENTITY); + return dest; + } + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + *

+ * In order to set the matrix to a translation transformation without post-multiplying + * it, use {@link #translation(float, float, float)}. + * + * @see #translation(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4x3f translate(float x, float y, float z) { + if ((properties & PROPERTY_IDENTITY) != 0) + return translation(x, y, z); + Matrix4x3f c = this; + c.m30 = c.m00 * x + c.m10 * y + c.m20 * z + c.m30; + c.m31 = c.m01 * x + c.m11 * y + c.m21 * z + c.m31; + c.m32 = c.m02 * x + c.m12 * y + c.m22 * z + c.m32; + c.properties &= ~(PROPERTY_IDENTITY); + return this; + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @return this + */ + public Matrix4x3f translateLocal(Vector3fc offset) { + return translateLocal(offset.x(), offset.y(), offset.z()); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(Vector3fc)}. + * + * @see #translation(Vector3fc) + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f translateLocal(Vector3fc offset, Matrix4x3f dest) { + return translateLocal(offset.x(), offset.y(), offset.z(), dest); + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(float, float, float)}. + * + * @see #translation(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f translateLocal(float x, float y, float z, Matrix4x3f dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m30 = m30 + x; + dest.m31 = m31 + y; + dest.m32 = m32 + z; + dest.properties = properties & ~(PROPERTY_IDENTITY); + return dest; + } + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + *

+ * In order to set the matrix to a translation transformation without pre-multiplying + * it, use {@link #translation(float, float, float)}. + * + * @see #translation(float, float, float) + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @return this + */ + public Matrix4x3f translateLocal(float x, float y, float z) { + return translateLocal(x, y, z, this); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(m00); + out.writeFloat(m01); + out.writeFloat(m02); + out.writeFloat(m10); + out.writeFloat(m11); + out.writeFloat(m12); + out.writeFloat(m20); + out.writeFloat(m21); + out.writeFloat(m22); + out.writeFloat(m30); + out.writeFloat(m31); + out.writeFloat(m32); + } + + public void readExternal(ObjectInput in) throws IOException { + m00 = in.readFloat(); + m01 = in.readFloat(); + m02 = in.readFloat(); + m10 = in.readFloat(); + m11 = in.readFloat(); + m12 = in.readFloat(); + m20 = in.readFloat(); + m21 = in.readFloat(); + m22 = in.readFloat(); + m30 = in.readFloat(); + m31 = in.readFloat(); + m32 = in.readFloat(); + determineProperties(); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(float, float, float, float, float, float, boolean) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f ortho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4x3f dest) { + // calculate right matrix elements + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm22 = (zZeroToOne ? 1.0f : 2.0f) / (zNear - zFar); + float rm30 = (left + right) / (left - right); + float rm31 = (top + bottom) / (bottom - top); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20 * rm22; + dest.m21 = m21 * rm22; + dest.m22 = m22 * rm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(float, float, float, float, float, float) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f ortho(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4x3f dest) { + return ortho(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system using the given NDC z range to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(float, float, float, float, float, float, boolean) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3f ortho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + return ortho(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho(float, float, float, float, float, float) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3f ortho(float left, float right, float bottom, float top, float zNear, float zFar) { + return ortho(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(float, float, float, float, float, float, boolean) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4x3f dest) { + // calculate right matrix elements + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm22 = (zZeroToOne ? 1.0f : 2.0f) / (zFar - zNear); + float rm30 = (left + right) / (left - right); + float rm31 = (top + bottom) / (bottom - top); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20 * rm22; + dest.m21 = m21 * rm22; + dest.m22 = m22 * rm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(float, float, float, float, float, float) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4x3f dest) { + return orthoLH(left, right, bottom, top, zNear, zFar, false, dest); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(float, float, float, float, float, float, boolean) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + return orthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrthoLH(float, float, float, float, float, float) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar) { + return orthoLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho(float, float, float, float, float, float, boolean) ortho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3f setOrtho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + MemUtil.INSTANCE.identity(this); + m00 = 2.0f / (right - left); + m11 = 2.0f / (top - bottom); + m22 = (zZeroToOne ? 1.0f : 2.0f) / (zNear - zFar); + m30 = (right + left) / (left - right); + m31 = (top + bottom) / (bottom - top); + m32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + properties = 0; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho(float, float, float, float, float, float) ortho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3f setOrtho(float left, float right, float bottom, float top, float zNear, float zFar) { + return setOrtho(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #orthoLH(float, float, float, float, float, float, boolean) orthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float, boolean) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3f setOrthoLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) { + MemUtil.INSTANCE.identity(this); + m00 = 2.0f / (right - left); + m11 = 2.0f / (top - bottom); + m22 = (zZeroToOne ? 1.0f : 2.0f) / (zFar - zNear); + m30 = (right + left) / (left - right); + m31 = (top + bottom) / (bottom - top); + m32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + properties = 0; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #orthoLH(float, float, float, float, float, float) orthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3f setOrthoLH(float left, float right, float bottom, float top, float zNear, float zFar) { + return setOrthoLH(left, right, bottom, top, zNear, zFar, false); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, boolean, Matrix4x3f) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(float, float, float, float, boolean) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4x3f orthoSymmetric(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4x3f dest) { + // calculate right matrix elements + float rm00 = 2.0f / width; + float rm11 = 2.0f / height; + float rm22 = (zZeroToOne ? 1.0f : 2.0f) / (zNear - zFar); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m20 * rm32 + m30; + dest.m31 = m21 * rm32 + m31; + dest.m32 = m22 * rm32 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20 * rm22; + dest.m21 = m21 * rm22; + dest.m22 = m22 * rm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, Matrix4x3f) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(float, float, float, float) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f orthoSymmetric(float width, float height, float zNear, float zFar, Matrix4x3f dest) { + return orthoSymmetric(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, boolean) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(float, float, float, float, boolean) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3f orthoSymmetric(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + return orthoSymmetric(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetric(float, float, float, float) setOrthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetric(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3f orthoSymmetric(float width, float height, float zNear, float zFar) { + return orthoSymmetric(width, height, zNear, zFar, false, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, boolean, Matrix4x3f) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(float, float, float, float, boolean) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + public Matrix4x3f orthoSymmetricLH(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4x3f dest) { + // calculate right matrix elements + float rm00 = 2.0f / width; + float rm11 = 2.0f / height; + float rm22 = (zZeroToOne ? 1.0f : 2.0f) / (zFar - zNear); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m20 * rm32 + m30; + dest.m31 = m21 * rm32 + m31; + dest.m32 = m22 * rm32 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20 * rm22; + dest.m21 = m21 * rm22; + dest.m22 = m22 * rm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, Matrix4x3f) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(float, float, float, float) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f orthoSymmetricLH(float width, float height, float zNear, float zFar, Matrix4x3f dest) { + return orthoSymmetricLH(width, height, zNear, zFar, false, dest); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, boolean) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(float, float, float, float, boolean) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3f orthoSymmetricLH(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + return orthoSymmetricLH(width, height, zNear, zFar, zZeroToOne, this); + } + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to a symmetric orthographic projection without post-multiplying it, + * use {@link #setOrthoSymmetricLH(float, float, float, float) setOrthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoSymmetricLH(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3f orthoSymmetricLH(float width, float height, float zNear, float zFar) { + return orthoSymmetricLH(width, height, zNear, zFar, false, this); + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system using the given NDC z range. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float, boolean) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetric(float, float, float, float, boolean) orthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetric(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3f setOrthoSymmetric(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + MemUtil.INSTANCE.identity(this); + m00 = 2.0f / width; + m11 = 2.0f / height; + m22 = (zZeroToOne ? 1.0f : 2.0f) / (zNear - zFar); + m32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + properties = 0; + return this; + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetric(float, float, float, float) orthoSymmetric()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetric(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3f setOrthoSymmetric(float width, float height, float zNear, float zFar) { + return setOrthoSymmetric(width, height, zNear, zFar, false); + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system using the given NDC z range. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float, boolean) setOrtho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetricLH(float, float, float, float, boolean) orthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetricLH(float, float, float, float, boolean) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return this + */ + public Matrix4x3f setOrthoSymmetricLH(float width, float height, float zNear, float zFar, boolean zZeroToOne) { + MemUtil.INSTANCE.identity(this); + m00 = 2.0f / width; + m11 = 2.0f / height; + m22 = (zZeroToOne ? 1.0f : 2.0f) / (zFar - zNear); + m32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + properties = 0; + return this; + } + + /** + * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1]. + *

+ * This method is equivalent to calling {@link #setOrthoLH(float, float, float, float, float, float) setOrthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * In order to apply the symmetric orthographic projection to an already existing transformation, + * use {@link #orthoSymmetricLH(float, float, float, float) orthoSymmetricLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoSymmetricLH(float, float, float, float) + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @return this + */ + public Matrix4x3f setOrthoSymmetricLH(float width, float height, float zNear, float zFar) { + return setOrthoSymmetricLH(width, height, zNear, zFar, false); + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix + * and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, Matrix4x3f) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2D(float, float, float, float) setOrtho()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float, Matrix4x3f) + * @see #setOrtho2D(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f ortho2D(float left, float right, float bottom, float top, Matrix4x3f dest) { + // calculate right matrix elements + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm30 = -(right + left) / (right - left); + float rm31 = -(top + bottom) / (top - bottom); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = -m20; + dest.m21 = -m21; + dest.m22 = -m22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2D(float, float, float, float) setOrtho2D()}. + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float) + * @see #setOrtho2D(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4x3f ortho2D(float left, float right, float bottom, float top) { + return ortho2D(left, right, bottom, top, this); + } + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, Matrix4x3f) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2DLH(float, float, float, float) setOrthoLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float, Matrix4x3f) + * @see #setOrtho2DLH(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f ortho2DLH(float left, float right, float bottom, float top, Matrix4x3f dest) { + // calculate right matrix elements + float rm00 = 2.0f / (right - left); + float rm11 = 2.0f / (top - bottom); + float rm30 = -(right + left) / (right - left); + float rm31 = -(top + bottom) / (top - bottom); + + // perform optimized multiplication + // compute the last column first, because other columns do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m32; + dest.m00 = m00 * rm00; + dest.m01 = m01 * rm00; + dest.m02 = m02 * rm00; + dest.m10 = m10 * rm11; + dest.m11 = m11 * rm11; + dest.m12 = m12 * rm11; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * In order to set the matrix to an orthographic projection without post-multiplying it, + * use {@link #setOrtho2DLH(float, float, float, float) setOrtho2DLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float) + * @see #setOrtho2DLH(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4x3f ortho2DLH(float left, float right, float bottom, float top) { + return ortho2DLH(left, right, bottom, top, this); + } + + /** + * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float) setOrtho()} with + * zNear=-1 and zFar=+1. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho2D(float, float, float, float) ortho2D()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrtho(float, float, float, float, float, float) + * @see #ortho2D(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4x3f setOrtho2D(float left, float right, float bottom, float top) { + MemUtil.INSTANCE.identity(this); + m00 = 2.0f / (right - left); + m11 = 2.0f / (top - bottom); + m22 = -1.0f; + m30 = -(right + left) / (right - left); + m31 = -(top + bottom) / (top - bottom); + properties = 0; + return this; + } + + /** + * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system. + *

+ * This method is equivalent to calling {@link #setOrtho(float, float, float, float, float, float) setOrthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * In order to apply the orthographic projection to an already existing transformation, + * use {@link #ortho2DLH(float, float, float, float) ortho2DLH()}. + *

+ * Reference: http://www.songho.ca + * + * @see #setOrthoLH(float, float, float, float, float, float) + * @see #ortho2DLH(float, float, float, float) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @return this + */ + public Matrix4x3f setOrtho2DLH(float left, float right, float bottom, float top) { + MemUtil.INSTANCE.identity(this); + m00 = 2.0f / (right - left); + m11 = 2.0f / (top - bottom); + m22 = 1.0f; + m30 = -(right + left) / (right - left); + m31 = -(top + bottom) / (top - bottom); + properties = 0; + return this; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3fc, Vector3fc, Vector3fc) lookAt} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3fc, Vector3fc) setLookAlong()}. + * + * @see #lookAlong(float, float, float, float, float, float) + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3f lookAlong(Vector3fc dir, Vector3fc up) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3fc, Vector3fc, Vector3fc) lookAt} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(Vector3fc, Vector3fc) setLookAlong()}. + * + * @see #lookAlong(float, float, float, float, float, float) + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f lookAlong(Vector3fc dir, Vector3fc up, Matrix4x3f dest) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(float, float, float, float, float, float) setLookAlong()} + * + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * @see #setLookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return setLookAlong(dirX, dirY, dirZ, upX, upY, upZ); + + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + float rm00 = leftX; + float rm01 = upnX; + float rm02 = dirX; + float rm10 = leftY; + float rm11 = upnY; + float rm12 = dirY; + float rm20 = leftZ; + float rm21 = upnZ; + float rm22 = dirZ; + + // perform optimized matrix multiplication + // introduce temporaries for dependent results + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set the rest of the matrix elements + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply a rotation transformation to this matrix to make -z point along dir. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to set the matrix to a lookalong transformation without post-multiplying it, + * use {@link #setLookAlong(float, float, float, float, float, float) setLookAlong()} + * + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * @see #setLookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + return lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * This is equivalent to calling + * {@link #setLookAt(Vector3fc, Vector3fc, Vector3fc) setLookAt()} + * with eye = (0, 0, 0) and center = dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(Vector3fc, Vector3fc)}. + * + * @see #setLookAlong(Vector3fc, Vector3fc) + * @see #lookAlong(Vector3fc, Vector3fc) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3f setLookAlong(Vector3fc dir, Vector3fc up) { + return setLookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a rotation transformation to make -z + * point along dir. + *

+ * This is equivalent to calling + * {@link #setLookAt(float, float, float, float, float, float, float, float, float) + * setLookAt()} with eye = (0, 0, 0) and center = dir. + *

+ * In order to apply the lookalong transformation to any previous existing transformation, + * use {@link #lookAlong(float, float, float, float, float, float) lookAlong()} + * + * @see #setLookAlong(float, float, float, float, float, float) + * @see #lookAlong(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f setLookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= -invDirLength; + dirY *= -invDirLength; + dirZ *= -invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + m00 = leftX; + m01 = upnX; + m02 = dirX; + m10 = leftY; + m11 = upnY; + m12 = dirY; + m20 = leftZ; + m21 = upnZ; + m22 = dirZ; + m30 = 0.0f; + m31 = 0.0f; + m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + + return this; + } + + /** + * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, that aligns + * -z with center - eye. + *

+ * In order to not make use of vectors to specify eye, center and up but use primitives, + * like in the GLU function, use {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt()} + * instead. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAt(Vector3fc, Vector3fc, Vector3fc) lookAt()}. + * + * @see #setLookAt(float, float, float, float, float, float, float, float, float) + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3f setLookAt(Vector3fc eye, Vector3fc center, Vector3fc up) { + return setLookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAt(float, float, float, float, float, float, float, float, float) lookAt}. + * + * @see #setLookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f setLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + m00 = leftX; + m01 = upnX; + m02 = dirX; + m10 = leftY; + m11 = upnY; + m12 = dirY; + m20 = leftZ; + m21 = upnZ; + m22 = dirZ; + m30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + m31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + m32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + properties = PROPERTY_ORTHONORMAL; + + return this; + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f lookAt(Vector3fc eye, Vector3fc center, Vector3fc up, Matrix4x3f dest) { + return lookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #lookAt(float, float, float, float, float, float, float, float, float) + * @see #setLookAlong(Vector3fc, Vector3fc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3f lookAt(Vector3fc eye, Vector3fc center, Vector3fc up) { + return lookAt(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt()}. + * + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAt(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f lookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + return lookAtGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + } + private Matrix4x3f lookAtGeneric(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4x3f dest) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = eyeX - centerX; + dirY = eyeY - centerY; + dirZ = eyeZ - centerZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + float rm00 = leftX; + float rm01 = upnX; + float rm02 = dirX; + float rm10 = leftY; + float rm11 = upnY; + float rm12 = dirY; + float rm20 = leftZ; + float rm21 = upnZ; + float rm22 = dirZ; + float rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + float rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + float rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + + // perform optimized matrix multiplication + // compute last column first, because others do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + // introduce temporaries for dependent results + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set the rest of the matrix elements + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAt(float, float, float, float, float, float, float, float, float) setLookAt()}. + * + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAt(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f lookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + return lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this); + } + + /** + * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, that aligns + * +z with center - eye. + *

+ * In order to not make use of vectors to specify eye, center and up but use primitives, + * like in the GLU function, use {@link #setLookAtLH(float, float, float, float, float, float, float, float, float) setLookAtLH()} + * instead. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAtLH(Vector3fc, Vector3fc, Vector3fc) lookAt()}. + * + * @see #setLookAtLH(float, float, float, float, float, float, float, float, float) + * @see #lookAtLH(Vector3fc, Vector3fc, Vector3fc) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3f setLookAtLH(Vector3fc eye, Vector3fc center, Vector3fc up) { + return setLookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * In order to apply the lookat transformation to a previous existing transformation, + * use {@link #lookAtLH(float, float, float, float, float, float, float, float, float) lookAtLH}. + * + * @see #setLookAtLH(Vector3fc, Vector3fc, Vector3fc) + * @see #lookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f setLookAtLH(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + m00 = leftX; + m01 = upnX; + m02 = dirX; + m10 = leftY; + m11 = upnY; + m12 = dirY; + m20 = leftZ; + m21 = upnZ; + m22 = dirZ; + m30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + m31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + m32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + properties = PROPERTY_ORTHONORMAL; + + return this; + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #lookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f lookAtLH(Vector3fc eye, Vector3fc center, Vector3fc up, Matrix4x3f dest) { + return lookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #lookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @return this + */ + public Matrix4x3f lookAtLH(Vector3fc eye, Vector3fc center, Vector3fc up) { + return lookAtLH(eye.x(), eye.y(), eye.z(), center.x(), center.y(), center.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(float, float, float, float, float, float, float, float, float) setLookAtLH()}. + * + * @see #lookAtLH(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f lookAtLH(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.setLookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + return lookAtLHGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest); + } + private Matrix4x3f lookAtLHGeneric(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ, Matrix4x3f dest) { + // Compute direction from position to lookAt + float dirX, dirY, dirZ; + dirX = centerX - eyeX; + dirY = centerY - eyeY; + dirZ = centerZ - eyeZ; + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLength; + dirY *= invDirLength; + dirZ *= invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * dirZ - upZ * dirY; + leftY = upZ * dirX - upX * dirZ; + leftZ = upX * dirY - upY * dirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirY * leftZ - dirZ * leftY; + float upnY = dirZ * leftX - dirX * leftZ; + float upnZ = dirX * leftY - dirY * leftX; + + // calculate right matrix elements + float rm00 = leftX; + float rm01 = upnX; + float rm02 = dirX; + float rm10 = leftY; + float rm11 = upnY; + float rm12 = dirY; + float rm20 = leftZ; + float rm21 = upnZ; + float rm22 = dirZ; + float rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ); + float rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ); + float rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ); + + // perform optimized matrix multiplication + // compute last column first, because others do not depend on it + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + // introduce temporaries for dependent results + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + // set the rest of the matrix elements + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a lookat transformation without post-multiplying it, + * use {@link #setLookAtLH(float, float, float, float, float, float, float, float, float) setLookAtLH()}. + * + * @see #lookAtLH(Vector3fc, Vector3fc, Vector3fc) + * @see #setLookAtLH(float, float, float, float, float, float, float, float, float) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f lookAtLH(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + return lookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotate(Quaternionfc quat, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.rotation(quat); + else if ((properties & PROPERTY_TRANSLATION) != 0) + return rotateTranslation(quat, dest); + return rotateGeneric(quat, dest); + } + private Matrix4x3f rotateGeneric(Quaternionfc quat, Matrix4x3f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = dxy - dzw; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4x3f rotate(Quaternionfc quat) { + return rotate(quat, this); + } + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateTranslation(Quaternionfc quat, Matrix4x3f dest) { + float w2 = quat.w() * quat.w(), x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(), z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(), dzw = zw + zw, xy = quat.x() * quat.y(), dxy = xy + xy; + float xz = quat.x() * quat.z(), dxz = xz + xz, yw = quat.y() * quat.w(), dyw = yw + yw; + float yz = quat.y() * quat.z(), dyz = yz + yz, xw = quat.x() * quat.w(), dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = dxy - dzw; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + dest.m20 = rm20; + dest.m21 = rm21; + dest.m22 = rm22; + dest.m00 = rm00; + dest.m01 = rm01; + dest.m02 = rm02; + dest.m10 = rm10; + dest.m11 = rm11; + dest.m12 = rm12; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateLocal(Quaternionfc quat, Matrix4x3f dest) { + float w2 = quat.w() * quat.w(); + float x2 = quat.x() * quat.x(); + float y2 = quat.y() * quat.y(); + float z2 = quat.z() * quat.z(); + float zw = quat.z() * quat.w(); + float xy = quat.x() * quat.y(); + float xz = quat.x() * quat.z(); + float yw = quat.y() * quat.w(); + float yz = quat.y() * quat.z(); + float xw = quat.x() * quat.w(); + float lm00 = w2 + x2 - z2 - y2; + float lm01 = xy + zw + zw + xy; + float lm02 = xz - yw + xz - yw; + float lm10 = -zw + xy - zw + xy; + float lm11 = y2 - z2 + w2 - x2; + float lm12 = yz + yz + xw + xw; + float lm20 = yw + xz + xz + yw; + float lm21 = yz + yz - xw - xw; + float lm22 = z2 - y2 - x2 + w2; + float nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02; + float nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02; + float nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02; + float nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12; + float nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12; + float nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12; + float nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22; + float nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22; + float nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22; + float nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32; + float nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32; + float nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = nm30; + dest.m31 = nm31; + dest.m32 = nm32; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * In order to set the matrix to a rotation transformation without pre-multiplying, + * use {@link #rotation(Quaternionfc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotation(Quaternionfc) + * + * @param quat + * the {@link Quaternionfc} + * @return this + */ + public Matrix4x3f rotateLocal(Quaternionfc quat) { + return rotateLocal(quat, this); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f}, to this matrix. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @return this + */ + public Matrix4x3f rotate(AxisAngle4f axisAngle) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(AxisAngle4f)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(AxisAngle4f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotate(AxisAngle4f axisAngle, Matrix4x3f dest) { + return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis-angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(float, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix4x3f rotate(float angle, Vector3fc axis) { + return rotate(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis-angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying, + * use {@link #rotation(float, Vector3fc)}. + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float) + * @see #rotation(float, Vector3fc) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotate(float angle, Vector3fc axis, Matrix4x3f dest) { + return rotate(angle, axis.x(), axis.y(), axis.z(), dest); + } + + public Matrix4x3f reflect(float a, float b, float c, float d, Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.reflection(a, b, c, d); + + float da = a + a, db = b + b, dc = c + c, dd = d + d; + float rm00 = 1.0f - da * a; + float rm01 = -da * b; + float rm02 = -da * c; + float rm10 = -db * a; + float rm11 = 1.0f - db * b; + float rm12 = -db * c; + float rm20 = -dc * a; + float rm21 = -dc * b; + float rm22 = 1.0f - dc * c; + float rm30 = -dd * a; + float rm31 = -dd * b; + float rm32 = -dd * c; + + // matrix multiplication + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + + return dest; + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4x3f reflect(float a, float b, float c, float d) { + return reflect(a, b, c, d, this); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @return this + */ + public Matrix4x3f reflect(float nx, float ny, float nz, float px, float py, float pz) { + return reflect(nx, ny, nz, px, py, pz, this); + } + + public Matrix4x3f reflect(float nx, float ny, float nz, float px, float py, float pz, Matrix4x3f dest) { + float invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz); + float nnx = nx * invLength; + float nny = ny * invLength; + float nnz = nz * invLength; + /* See: http://mathworld.wolfram.com/Plane.html */ + return reflect(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz, dest); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @return this + */ + public Matrix4x3f reflect(Vector3fc normal, Vector3fc point) { + return reflect(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z()); + } + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation and a point on the plane. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation + * @param point + * a point on the plane + * @return this + */ + public Matrix4x3f reflect(Quaternionfc orientation, Vector3fc point) { + return reflect(orientation, point, this); + } + + public Matrix4x3f reflect(Quaternionfc orientation, Vector3fc point, Matrix4x3f dest) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + float normalX = (float) (orientation.x() * num3 + orientation.w() * num2); + float normalY = (float) (orientation.y() * num3 - orientation.w() * num1); + float normalZ = (float) (1.0 - (orientation.x() * num1 + orientation.y() * num2)); + return reflect(normalX, normalY, normalZ, point.x(), point.y(), point.z(), dest); + } + + public Matrix4x3f reflect(Vector3fc normal, Vector3fc point, Matrix4x3f dest) { + return reflect(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z(), dest); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4x3f reflection(float a, float b, float c, float d) { + float da = a + a, db = b + b, dc = c + c, dd = d + d; + m00 = 1.0f - da * a; + m01 = -da * b; + m02 = -da * c; + m10 = -db * a; + m11 = 1.0f - db * b; + m12 = -db * c; + m20 = -dc * a; + m21 = -dc * b; + m22 = 1.0f - dc * c; + m30 = -dd * a; + m31 = -dd * b; + m32 = -dd * c; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the plane normal and a point on the plane. + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @return this + */ + public Matrix4x3f reflection(float nx, float ny, float nz, float px, float py, float pz) { + float invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz); + float nnx = nx * invLength; + float nny = ny * invLength; + float nnz = nz * invLength; + /* See: http://mathworld.wolfram.com/Plane.html */ + return reflection(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about the given plane + * specified via the plane normal and a point on the plane. + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @return this + */ + public Matrix4x3f reflection(Vector3fc normal, Vector3fc point) { + return reflection(normal.x(), normal.y(), normal.z(), point.x(), point.y(), point.z()); + } + + /** + * Set this matrix to a mirror/reflection transformation that reflects about a plane + * specified via the plane orientation and a point on the plane. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + * + * @param orientation + * the plane orientation + * @param point + * a point on the plane + * @return this + */ + public Matrix4x3f reflection(Quaternionfc orientation, Vector3fc point) { + double num1 = orientation.x() + orientation.x(); + double num2 = orientation.y() + orientation.y(); + double num3 = orientation.z() + orientation.z(); + float normalX = (float) (orientation.x() * num3 + orientation.w() * num2); + float normalY = (float) (orientation.y() * num3 - orientation.w() * num1); + float normalZ = (float) (1.0 - (orientation.x() * num1 + orientation.y() * num2)); + return reflection(normalX, normalY, normalZ, point.x(), point.y(), point.z()); + } + + public Vector4f getRow(int row, Vector4f dest) throws IndexOutOfBoundsException { + switch (row) { + case 0: + dest.x = m00; + dest.y = m10; + dest.z = m20; + dest.w = m30; + break; + case 1: + dest.x = m01; + dest.y = m11; + dest.z = m21; + dest.w = m31; + break; + case 2: + dest.x = m02; + dest.y = m12; + dest.z = m22; + dest.w = m32; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param src + * the row components to set + * @return this + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + public Matrix4x3f setRow(int row, Vector4fc src) throws IndexOutOfBoundsException { + switch (row) { + case 0: + this.m00 = src.x(); + this.m10 = src.y(); + this.m20 = src.z(); + this.m30 = src.w(); + break; + case 1: + this.m01 = src.x(); + this.m11 = src.y(); + this.m21 = src.z(); + this.m31 = src.w(); + break; + case 2: + this.m02 = src.x(); + this.m12 = src.y(); + this.m22 = src.z(); + this.m32 = src.w(); + break; + default: + throw new IndexOutOfBoundsException(); + } + properties = 0; + return this; + } + + public Vector3f getColumn(int column, Vector3f dest) throws IndexOutOfBoundsException { + switch (column) { + case 0: + dest.x = m00; + dest.y = m01; + dest.z = m02; + break; + case 1: + dest.x = m10; + dest.y = m11; + dest.z = m12; + break; + case 2: + dest.x = m20; + dest.y = m21; + dest.z = m22; + break; + case 3: + dest.x = m30; + dest.y = m31; + dest.z = m32; + break; + default: + throw new IndexOutOfBoundsException(); + } + return dest; + } + + /** + * Set the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..3] + * @param src + * the column components to set + * @return this + * @throws IndexOutOfBoundsException if column is not in [0..3] + */ + public Matrix4x3f setColumn(int column, Vector3fc src) throws IndexOutOfBoundsException { + switch (column) { + case 0: + this.m00 = src.x(); + this.m01 = src.y(); + this.m02 = src.z(); + break; + case 1: + this.m10 = src.x(); + this.m11 = src.y(); + this.m12 = src.z(); + break; + case 2: + this.m20 = src.x(); + this.m21 = src.y(); + this.m22 = src.z(); + break; + case 3: + this.m30 = src.x(); + this.m31 = src.y(); + this.m32 = src.z(); + break; + default: + throw new IndexOutOfBoundsException(); + } + properties = 0; + return this; + } + + /** + * Compute a normal matrix from the left 3x3 submatrix of this + * and store it into the left 3x3 submatrix of this. + * All other values of this will be set to {@link #identity() identity}. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link #set3x3(Matrix4x3fc)} to set a given Matrix4x3f to only the left 3x3 submatrix + * of this matrix. + * + * @see #set3x3(Matrix4x3fc) + * + * @return this + */ + public Matrix4x3f normal() { + return normal(this); + } + + /** + * Compute a normal matrix from the left 3x3 submatrix of this + * and store it into the left 3x3 submatrix of dest. + * All other values of dest will be set to {@link #identity() identity}. + *

+ * The normal matrix of m is the transpose of the inverse of m. + *

+ * Please note that, if this is an orthogonal matrix or a matrix whose columns are orthogonal vectors, + * then this method need not be invoked, since in that case this itself is its normal matrix. + * In that case, use {@link #set3x3(Matrix4x3fc)} to set a given Matrix4x3f to only the left 3x3 submatrix + * of this matrix. + * + * @see #set3x3(Matrix4x3fc) + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f normal(Matrix4x3f dest) { + if ((properties & PROPERTY_IDENTITY) != 0) + return dest.identity(); + else if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalOrthonormal(dest); + return normalGeneric(dest); + } + private Matrix4x3f normalOrthonormal(Matrix4x3f dest) { + if (dest != this) + dest.set(this); + return dest._properties(PROPERTY_ORTHONORMAL); + } + private Matrix4x3f normalGeneric(Matrix4x3f dest) { + float m00m11 = m00 * m11; + float m01m10 = m01 * m10; + float m02m10 = m02 * m10; + float m00m12 = m00 * m12; + float m01m12 = m01 * m12; + float m02m11 = m02 * m11; + float det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + float s = 1.0f / det; + /* Invert and transpose in one go */ + float nm00 = (m11 * m22 - m21 * m12) * s; + float nm01 = (m20 * m12 - m10 * m22) * s; + float nm02 = (m10 * m21 - m20 * m11) * s; + float nm10 = (m21 * m02 - m01 * m22) * s; + float nm11 = (m00 * m22 - m20 * m02) * s; + float nm12 = (m20 * m01 - m00 * m21) * s; + float nm20 = (m01m12 - m02m11) * s; + float nm21 = (m02m10 - m00m12) * s; + float nm22 = (m00m11 - m01m10) * s; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = 0.0f; + dest.m31 = 0.0f; + dest.m32 = 0.0f; + dest.properties = properties & ~PROPERTY_TRANSLATION; + return dest; + } + + public Matrix3f normal(Matrix3f dest) { + if ((properties & PROPERTY_ORTHONORMAL) != 0) + return normalOrthonormal(dest); + return normalGeneric(dest); + } + private Matrix3f normalOrthonormal(Matrix3f dest) { + return dest.set(this); + } + private Matrix3f normalGeneric(Matrix3f dest) { + float m00m11 = m00 * m11; + float m01m10 = m01 * m10; + float m02m10 = m02 * m10; + float m00m12 = m00 * m12; + float m01m12 = m01 * m12; + float m02m11 = m02 * m11; + float det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20; + float s = 1.0f / det; + /* Invert and transpose in one go */ + dest.m00((m11 * m22 - m21 * m12) * s); + dest.m01((m20 * m12 - m10 * m22) * s); + dest.m02((m10 * m21 - m20 * m11) * s); + dest.m10((m21 * m02 - m01 * m22) * s); + dest.m11((m00 * m22 - m20 * m02) * s); + dest.m12((m20 * m01 - m00 * m21) * s); + dest.m20((m01m12 - m02m11) * s); + dest.m21((m02m10 - m00m12) * s); + dest.m22((m00m11 - m01m10) * s); + return dest; + } + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this. + *

+ * The cofactor matrix can be used instead of {@link #normal()} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @return this + */ + public Matrix4x3f cofactor3x3() { + return cofactor3x3(this); + } + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this + * and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix3f cofactor3x3(Matrix3f dest) { + dest.m00 = m11 * m22 - m21 * m12; + dest.m01 = m20 * m12 - m10 * m22; + dest.m02 = m10 * m21 - m20 * m11; + dest.m10 = m21 * m02 - m01 * m22; + dest.m11 = m00 * m22 - m20 * m02; + dest.m12 = m20 * m01 - m00 * m21; + dest.m20 = m01 * m12 - m02 * m11; + dest.m21 = m02 * m10 - m00 * m12; + dest.m22 = m00 * m11 - m01 * m10; + return dest; + } + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this + * and store it into dest. + * All other values of dest will be set to {@link #identity() identity}. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix4x3f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f cofactor3x3(Matrix4x3f dest) { + float nm00 = m11 * m22 - m21 * m12; + float nm01 = m20 * m12 - m10 * m22; + float nm02 = m10 * m21 - m20 * m11; + float nm10 = m21 * m02 - m01 * m22; + float nm11 = m00 * m22 - m20 * m02; + float nm12 = m20 * m01 - m00 * m21; + float nm20 = m01 * m12 - m11 * m02; + float nm21 = m02 * m10 - m12 * m00; + float nm22 = m00 * m11 - m10 * m01; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m30 = 0.0f; + dest.m31 = 0.0f; + dest.m32 = 0.0f; + dest.properties = properties & ~PROPERTY_TRANSLATION; + return dest; + } + + /** + * Normalize the left 3x3 submatrix of this matrix. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @return this + */ + public Matrix4x3f normalize3x3() { + return normalize3x3(this); + } + + public Matrix4x3f normalize3x3(Matrix4x3f dest) { + float invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + float invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + float invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + dest.m00 = m00 * invXlen; dest.m01 = m01 * invXlen; dest.m02 = m02 * invXlen; + dest.m10 = m10 * invYlen; dest.m11 = m11 * invYlen; dest.m12 = m12 * invYlen; + dest.m20 = m20 * invZlen; dest.m21 = m21 * invZlen; dest.m22 = m22 * invZlen; + dest.properties = properties; + return dest; + } + + public Matrix3f normalize3x3(Matrix3f dest) { + float invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + float invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + float invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + dest.m00(m00 * invXlen); dest.m01(m01 * invXlen); dest.m02(m02 * invXlen); + dest.m10(m10 * invYlen); dest.m11(m11 * invYlen); dest.m12(m12 * invYlen); + dest.m20(m20 * invZlen); dest.m21(m21 * invZlen); dest.m22(m22 * invZlen); + return dest; + } + + public Vector4f frustumPlane(int which, Vector4f dest) { + switch (which) { + case PLANE_NX: + dest.set(m00, m10, m20, 1.0f + m30).normalize(); + break; + case PLANE_PX: + dest.set(-m00, -m10, -m20, 1.0f - m30).normalize(); + break; + case PLANE_NY: + dest.set(m01, m11, m21, 1.0f + m31).normalize(); + break; + case PLANE_PY: + dest.set(-m01, -m11, -m21, 1.0f - m31).normalize(); + break; + case PLANE_NZ: + dest.set(m02, m12, m22, 1.0f + m32).normalize(); + break; + case PLANE_PZ: + dest.set(-m02, -m12, -m22, 1.0f - m32).normalize(); + break; + default: + throw new IllegalArgumentException("which"); //$NON-NLS-1$ + } + return dest; + } + + public Vector3f positiveZ(Vector3f dir) { + dir.x = m10 * m21 - m11 * m20; + dir.y = m20 * m01 - m21 * m00; + dir.z = m00 * m11 - m01 * m10; + return dir.normalize(dir); + } + + public Vector3f normalizedPositiveZ(Vector3f dir) { + dir.x = m02; + dir.y = m12; + dir.z = m22; + return dir; + } + + public Vector3f positiveX(Vector3f dir) { + dir.x = m11 * m22 - m12 * m21; + dir.y = m02 * m21 - m01 * m22; + dir.z = m01 * m12 - m02 * m11; + return dir.normalize(dir); + } + + public Vector3f normalizedPositiveX(Vector3f dir) { + dir.x = m00; + dir.y = m10; + dir.z = m20; + return dir; + } + + public Vector3f positiveY(Vector3f dir) { + dir.x = m12 * m20 - m10 * m22; + dir.y = m00 * m22 - m02 * m20; + dir.z = m02 * m10 - m00 * m12; + return dir.normalize(dir); + } + + public Vector3f normalizedPositiveY(Vector3f dir) { + dir.x = m01; + dir.y = m11; + dir.z = m21; + return dir; + } + + public Vector3f origin(Vector3f origin) { + float a = m00 * m11 - m01 * m10; + float b = m00 * m12 - m02 * m10; + float d = m01 * m12 - m02 * m11; + float g = m20 * m31 - m21 * m30; + float h = m20 * m32 - m22 * m30; + float j = m21 * m32 - m22 * m31; + origin.x = -m10 * j + m11 * h - m12 * g; + origin.y = m00 * j - m01 * h + m02 * g; + origin.z = -m30 * d + m31 * b - m32 * a; + return origin; + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction light. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param light + * the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4x3f shadow(Vector4fc light, float a, float b, float c, float d) { + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, this); + } + + public Matrix4x3f shadow(Vector4fc light, float a, float b, float c, float d, Matrix4x3f dest) { + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW). + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param lightX + * the x-component of the light's vector + * @param lightY + * the y-component of the light's vector + * @param lightZ + * the z-component of the light's vector + * @param lightW + * the w-component of the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @return this + */ + public Matrix4x3f shadow(float lightX, float lightY, float lightZ, float lightW, float a, float b, float c, float d) { + return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, this); + } + + public Matrix4x3f shadow(float lightX, float lightY, float lightZ, float lightW, float a, float b, float c, float d, Matrix4x3f dest) { + // normalize plane + float invPlaneLen = Math.invsqrt(a*a + b*b + c*c); + float an = a * invPlaneLen; + float bn = b * invPlaneLen; + float cn = c * invPlaneLen; + float dn = d * invPlaneLen; + + float dot = an * lightX + bn * lightY + cn * lightZ + dn * lightW; + + // compute right matrix elements + float rm00 = dot - an * lightX; + float rm01 = -an * lightY; + float rm02 = -an * lightZ; + float rm03 = -an * lightW; + float rm10 = -bn * lightX; + float rm11 = dot - bn * lightY; + float rm12 = -bn * lightZ; + float rm13 = -bn * lightW; + float rm20 = -cn * lightX; + float rm21 = -cn * lightY; + float rm22 = dot - cn * lightZ; + float rm23 = -cn * lightW; + float rm30 = -dn * lightX; + float rm31 = -dn * lightY; + float rm32 = -dn * lightZ; + float rm33 = dot - dn * lightW; + + // matrix multiplication + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02 + m30 * rm03; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02 + m31 * rm03; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02 + m32 * rm03; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12 + m30 * rm13; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12 + m31 * rm13; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12 + m32 * rm13; + float nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30 * rm23; + float nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31 * rm23; + float nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32 * rm23; + dest.m30 = m00 * rm30 + m10 * rm31 + m20 * rm32 + m30 * rm33; + dest.m31 = m01 * rm30 + m11 * rm31 + m21 * rm32 + m31 * rm33; + dest.m32 = m02 * rm30 + m12 * rm31 + m22 * rm32 + m32 * rm33; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL); + + return dest; + } + + public Matrix4x3f shadow(Vector4fc light, Matrix4x3fc planeTransform, Matrix4x3f dest) { + // compute plane equation by transforming (y = 0) + float a = planeTransform.m10(); + float b = planeTransform.m11(); + float c = planeTransform.m12(); + float d = -a * planeTransform.m30() - b * planeTransform.m31() - c * planeTransform.m32(); + return shadow(light.x(), light.y(), light.z(), light.w(), a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction light. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param light + * the light's vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @return this + */ + public Matrix4x3f shadow(Vector4fc light, Matrix4x3fc planeTransform) { + return shadow(light, planeTransform, this); + } + + public Matrix4x3f shadow(float lightX, float lightY, float lightZ, float lightW, Matrix4x3fc planeTransform, Matrix4x3f dest) { + // compute plane equation by transforming (y = 0) + float a = planeTransform.m10(); + float b = planeTransform.m11(); + float c = planeTransform.m12(); + float d = -a * planeTransform.m30() - b * planeTransform.m31() - c * planeTransform.m32(); + return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, dest); + } + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW). + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param lightX + * the x-component of the light vector + * @param lightY + * the y-component of the light vector + * @param lightZ + * the z-component of the light vector + * @param lightW + * the w-component of the light vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @return this + */ + public Matrix4x3f shadow(float lightX, float lightY, float lightZ, float lightW, Matrix4x3f planeTransform) { + return shadow(lightX, lightY, lightZ, lightW, planeTransform, this); + } + + /** + * Set this matrix to a cylindrical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos while constraining a cylindrical rotation around the given up vector. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @param up + * the rotation axis (must be {@link Vector3f#normalize() normalized}) + * @return this + */ + public Matrix4x3f billboardCylindrical(Vector3fc objPos, Vector3fc targetPos, Vector3fc up) { + float dirX = targetPos.x() - objPos.x(); + float dirY = targetPos.y() - objPos.y(); + float dirZ = targetPos.z() - objPos.z(); + // left = up x dir + float leftX = up.y() * dirZ - up.z() * dirY; + float leftY = up.z() * dirX - up.x() * dirZ; + float leftZ = up.x() * dirY - up.y() * dirX; + // normalize left + float invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLen; + leftY *= invLeftLen; + leftZ *= invLeftLen; + // recompute dir by constraining rotation around 'up' + // dir = left x up + dirX = leftY * up.z() - leftZ * up.y(); + dirY = leftZ * up.x() - leftX * up.z(); + dirZ = leftX * up.y() - leftY * up.x(); + // normalize dir + float invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLen; + dirY *= invDirLen; + dirZ *= invDirLen; + // set matrix elements + m00 = leftX; + m01 = leftY; + m02 = leftZ; + m10 = up.x(); + m11 = up.y(); + m12 = up.z(); + m20 = dirX; + m21 = dirY; + m22 = dirZ; + m30 = objPos.x(); + m31 = objPos.y(); + m32 = objPos.z(); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + *

+ * If preserving an up vector is not necessary when rotating the +Z axis, then a shortest arc rotation can be obtained + * using {@link #billboardSpherical(Vector3fc, Vector3fc)}. + * + * @see #billboardSpherical(Vector3fc, Vector3fc) + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @param up + * the up axis used to orient the object + * @return this + */ + public Matrix4x3f billboardSpherical(Vector3fc objPos, Vector3fc targetPos, Vector3fc up) { + float dirX = targetPos.x() - objPos.x(); + float dirY = targetPos.y() - objPos.y(); + float dirZ = targetPos.z() - objPos.z(); + // normalize dir + float invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + dirX *= invDirLen; + dirY *= invDirLen; + dirZ *= invDirLen; + // left = up x dir + float leftX = up.y() * dirZ - up.z() * dirY; + float leftY = up.z() * dirX - up.x() * dirZ; + float leftZ = up.x() * dirY - up.y() * dirX; + // normalize left + float invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLen; + leftY *= invLeftLen; + leftZ *= invLeftLen; + // up = dir x left + float upX = dirY * leftZ - dirZ * leftY; + float upY = dirZ * leftX - dirX * leftZ; + float upZ = dirX * leftY - dirY * leftX; + // set matrix elements + m00 = leftX; + m01 = leftY; + m02 = leftZ; + m10 = upX; + m11 = upY; + m12 = upZ; + m20 = dirX; + m21 = dirY; + m22 = dirZ; + m30 = objPos.x(); + m31 = objPos.y(); + m32 = objPos.z(); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position objPos towards + * a target position at targetPos using a shortest arc rotation by not preserving any up vector of the object. + *

+ * This method can be used to create the complete model transformation for a given object, including the translation of the object to + * its position objPos. + *

+ * In order to specify an up vector which needs to be maintained when rotating the +Z axis of the object, + * use {@link #billboardSpherical(Vector3fc, Vector3fc, Vector3fc)}. + * + * @see #billboardSpherical(Vector3fc, Vector3fc, Vector3fc) + * + * @param objPos + * the position of the object to rotate towards targetPos + * @param targetPos + * the position of the target (for example the camera) towards which to rotate the object + * @return this + */ + public Matrix4x3f billboardSpherical(Vector3fc objPos, Vector3fc targetPos) { + float toDirX = targetPos.x() - objPos.x(); + float toDirY = targetPos.y() - objPos.y(); + float toDirZ = targetPos.z() - objPos.z(); + float x = -toDirY; + float y = toDirX; + float w = Math.sqrt(toDirX * toDirX + toDirY * toDirY + toDirZ * toDirZ) + toDirZ; + float invNorm = Math.invsqrt(x * x + y * y + w * w); + x *= invNorm; + y *= invNorm; + w *= invNorm; + float q00 = (x + x) * x; + float q11 = (y + y) * y; + float q01 = (x + x) * y; + float q03 = (x + x) * w; + float q13 = (y + y) * w; + m00 = 1.0f - q11; + m01 = q01; + m02 = -q13; + m10 = q01; + m11 = 1.0f - q00; + m12 = q03; + m20 = q13; + m21 = -q03; + m22 = 1.0f - q11 - q00; + m30 = objPos.x(); + m31 = objPos.y(); + m32 = objPos.z(); + properties = PROPERTY_ORTHONORMAL; + return this; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(m00); + result = prime * result + Float.floatToIntBits(m01); + result = prime * result + Float.floatToIntBits(m02); + result = prime * result + Float.floatToIntBits(m10); + result = prime * result + Float.floatToIntBits(m11); + result = prime * result + Float.floatToIntBits(m12); + result = prime * result + Float.floatToIntBits(m20); + result = prime * result + Float.floatToIntBits(m21); + result = prime * result + Float.floatToIntBits(m22); + result = prime * result + Float.floatToIntBits(m30); + result = prime * result + Float.floatToIntBits(m31); + result = prime * result + Float.floatToIntBits(m32); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof Matrix4x3f)) + return false; + Matrix4x3f other = (Matrix4x3f) obj; + if (Float.floatToIntBits(m00) != Float.floatToIntBits(other.m00)) + return false; + if (Float.floatToIntBits(m01) != Float.floatToIntBits(other.m01)) + return false; + if (Float.floatToIntBits(m02) != Float.floatToIntBits(other.m02)) + return false; + if (Float.floatToIntBits(m10) != Float.floatToIntBits(other.m10)) + return false; + if (Float.floatToIntBits(m11) != Float.floatToIntBits(other.m11)) + return false; + if (Float.floatToIntBits(m12) != Float.floatToIntBits(other.m12)) + return false; + if (Float.floatToIntBits(m20) != Float.floatToIntBits(other.m20)) + return false; + if (Float.floatToIntBits(m21) != Float.floatToIntBits(other.m21)) + return false; + if (Float.floatToIntBits(m22) != Float.floatToIntBits(other.m22)) + return false; + if (Float.floatToIntBits(m30) != Float.floatToIntBits(other.m30)) + return false; + if (Float.floatToIntBits(m31) != Float.floatToIntBits(other.m31)) + return false; + if (Float.floatToIntBits(m32) != Float.floatToIntBits(other.m32)) + return false; + return true; + } + + public boolean equals(Matrix4x3fc m, float delta) { + if (this == m) + return true; + if (m == null) + return false; + if (!(m instanceof Matrix4x3f)) + return false; + if (!Runtime.equals(m00, m.m00(), delta)) + return false; + if (!Runtime.equals(m01, m.m01(), delta)) + return false; + if (!Runtime.equals(m02, m.m02(), delta)) + return false; + if (!Runtime.equals(m10, m.m10(), delta)) + return false; + if (!Runtime.equals(m11, m.m11(), delta)) + return false; + if (!Runtime.equals(m12, m.m12(), delta)) + return false; + if (!Runtime.equals(m20, m.m20(), delta)) + return false; + if (!Runtime.equals(m21, m.m21(), delta)) + return false; + if (!Runtime.equals(m22, m.m22(), delta)) + return false; + if (!Runtime.equals(m30, m.m30(), delta)) + return false; + if (!Runtime.equals(m31, m.m31(), delta)) + return false; + if (!Runtime.equals(m32, m.m32(), delta)) + return false; + return true; + } + + public Matrix4x3f pick(float x, float y, float width, float height, int[] viewport, Matrix4x3f dest) { + float sx = viewport[2] / width; + float sy = viewport[3] / height; + float tx = (viewport[2] + 2.0f * (viewport[0] - x)) / width; + float ty = (viewport[3] + 2.0f * (viewport[1] - y)) / height; + dest.m30 = m00 * tx + m10 * ty + m30; + dest.m31 = m01 * tx + m11 * ty + m31; + dest.m32 = m02 * tx + m12 * ty + m32; + dest.m00 = m00 * sx; + dest.m01 = m01 * sx; + dest.m02 = m02 * sx; + dest.m10 = m10 * sy; + dest.m11 = m11 * sy; + dest.m12 = m12 * sy; + dest.properties = 0; + return dest; + } + + /** + * Apply a picking transformation to this matrix using the given window coordinates (x, y) as the pick center + * and the given (width, height) as the size of the picking region in window coordinates. + * + * @param x + * the x coordinate of the picking region center in window coordinates + * @param y + * the y coordinate of the picking region center in window coordinates + * @param width + * the width of the picking region in window coordinates + * @param height + * the height of the picking region in window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @return this + */ + public Matrix4x3f pick(float x, float y, float width, float height, int[] viewport) { + return pick(x, y, width, height, viewport, this); + } + + /** + * Exchange the values of this matrix with the given other matrix. + * + * @param other + * the other matrix to exchange the values with + * @return this + */ + public Matrix4x3f swap(Matrix4x3f other) { + MemUtil.INSTANCE.swap(this, other); + int props = properties; + this.properties = other.properties; + other.properties = props; + return this; + } + + public Matrix4x3f arcball(float radius, float centerX, float centerY, float centerZ, float angleX, float angleY, Matrix4x3f dest) { + float m30 = m20 * -radius + this.m30; + float m31 = m21 * -radius + this.m31; + float m32 = m22 * -radius + this.m32; + float sin = Math.sin(angleX); + float cos = Math.cosFromSin(sin, angleX); + float nm10 = m10 * cos + m20 * sin; + float nm11 = m11 * cos + m21 * sin; + float nm12 = m12 * cos + m22 * sin; + float m20 = this.m20 * cos - m10 * sin; + float m21 = this.m21 * cos - m11 * sin; + float m22 = this.m22 * cos - m12 * sin; + sin = Math.sin(angleY); + cos = Math.cosFromSin(sin, angleY); + float nm00 = m00 * cos - m20 * sin; + float nm01 = m01 * cos - m21 * sin; + float nm02 = m02 * cos - m22 * sin; + float nm20 = m00 * sin + m20 * cos; + float nm21 = m01 * sin + m21 * cos; + float nm22 = m02 * sin + m22 * cos; + dest.m30 = -nm00 * centerX - nm10 * centerY - nm20 * centerZ + m30; + dest.m31 = -nm01 * centerX - nm11 * centerY - nm21 * centerZ + m31; + dest.m32 = -nm02 * centerX - nm12 * centerY - nm22 * centerZ + m32; + dest.m20 = nm20; + dest.m21 = nm21; + dest.m22 = nm22; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + public Matrix4x3f arcball(float radius, Vector3fc center, float angleX, float angleY, Matrix4x3f dest) { + return arcball(radius, center.x(), center.y(), center.z(), angleX, angleY, dest); + } + + /** + * Apply an arcball view transformation to this matrix with the given radius and center (centerX, centerY, centerZ) + * position of the arcball and the specified X and Y rotation angles. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ) + * + * @param radius + * the arcball radius + * @param centerX + * the x coordinate of the center position of the arcball + * @param centerY + * the y coordinate of the center position of the arcball + * @param centerZ + * the z coordinate of the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @return this + */ + public Matrix4x3f arcball(float radius, float centerX, float centerY, float centerZ, float angleX, float angleY) { + return arcball(radius, centerX, centerY, centerZ, angleX, angleY, this); + } + + /** + * Apply an arcball view transformation to this matrix with the given radius and center + * position of the arcball and the specified X and Y rotation angles. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z) + * + * @param radius + * the arcball radius + * @param center + * the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @return this + */ + public Matrix4x3f arcball(float radius, Vector3fc center, float angleX, float angleY) { + return arcball(radius, center.x(), center.y(), center.z(), angleX, angleY, this); + } + + public Matrix4x3f transformAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, Vector3f outMin, Vector3f outMax) { + float xax = m00 * minX, xay = m01 * minX, xaz = m02 * minX; + float xbx = m00 * maxX, xby = m01 * maxX, xbz = m02 * maxX; + float yax = m10 * minY, yay = m11 * minY, yaz = m12 * minY; + float ybx = m10 * maxY, yby = m11 * maxY, ybz = m12 * maxY; + float zax = m20 * minZ, zay = m21 * minZ, zaz = m22 * minZ; + float zbx = m20 * maxZ, zby = m21 * maxZ, zbz = m22 * maxZ; + float xminx, xminy, xminz, yminx, yminy, yminz, zminx, zminy, zminz; + float xmaxx, xmaxy, xmaxz, ymaxx, ymaxy, ymaxz, zmaxx, zmaxy, zmaxz; + if (xax < xbx) { + xminx = xax; + xmaxx = xbx; + } else { + xminx = xbx; + xmaxx = xax; + } + if (xay < xby) { + xminy = xay; + xmaxy = xby; + } else { + xminy = xby; + xmaxy = xay; + } + if (xaz < xbz) { + xminz = xaz; + xmaxz = xbz; + } else { + xminz = xbz; + xmaxz = xaz; + } + if (yax < ybx) { + yminx = yax; + ymaxx = ybx; + } else { + yminx = ybx; + ymaxx = yax; + } + if (yay < yby) { + yminy = yay; + ymaxy = yby; + } else { + yminy = yby; + ymaxy = yay; + } + if (yaz < ybz) { + yminz = yaz; + ymaxz = ybz; + } else { + yminz = ybz; + ymaxz = yaz; + } + if (zax < zbx) { + zminx = zax; + zmaxx = zbx; + } else { + zminx = zbx; + zmaxx = zax; + } + if (zay < zby) { + zminy = zay; + zmaxy = zby; + } else { + zminy = zby; + zmaxy = zay; + } + if (zaz < zbz) { + zminz = zaz; + zmaxz = zbz; + } else { + zminz = zbz; + zmaxz = zaz; + } + outMin.x = xminx + yminx + zminx + m30; + outMin.y = xminy + yminy + zminy + m31; + outMin.z = xminz + yminz + zminz + m32; + outMax.x = xmaxx + ymaxx + zmaxx + m30; + outMax.y = xmaxy + ymaxy + zmaxy + m31; + outMax.z = xmaxz + ymaxz + zmaxz + m32; + return this; + } + + public Matrix4x3f transformAab(Vector3fc min, Vector3fc max, Vector3f outMin, Vector3f outMax) { + return transformAab(min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), outMin, outMax); + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Matrix4x3f lerp(Matrix4x3fc other, float t) { + return lerp(other, t, this); + } + + public Matrix4x3f lerp(Matrix4x3fc other, float t, Matrix4x3f dest) { + dest.m00 = Math.fma(other.m00() - m00, t, m00); + dest.m01 = Math.fma(other.m01() - m01, t, m01); + dest.m02 = Math.fma(other.m02() - m02, t, m02); + dest.m10 = Math.fma(other.m10() - m10, t, m10); + dest.m11 = Math.fma(other.m11() - m11, t, m11); + dest.m12 = Math.fma(other.m12() - m12, t, m12); + dest.m20 = Math.fma(other.m20() - m20, t, m20); + dest.m21 = Math.fma(other.m21() - m21, t, m21); + dest.m22 = Math.fma(other.m22() - m22, t, m22); + dest.m30 = Math.fma(other.m30() - m30, t, m30); + dest.m31 = Math.fma(other.m31() - m31, t, m31); + dest.m32 = Math.fma(other.m32() - m32, t, m32); + dest.properties = properties & other.properties(); + return dest; + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3fc, Vector3fc) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix4x3f().lookAt(new Vector3f(), new Vector3f(dir).negate(), up).invert(), dest) + * + * @see #rotateTowards(float, float, float, float, float, float, Matrix4x3f) + * @see #rotationTowards(Vector3fc, Vector3fc) + * + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateTowards(Vector3fc dir, Vector3fc up, Matrix4x3f dest) { + return rotateTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(Vector3fc, Vector3fc) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix4x3f().lookAt(new Vector3f(), new Vector3f(dir).negate(), up).invert()) + * + * @see #rotateTowards(float, float, float, float, float, float) + * @see #rotationTowards(Vector3fc, Vector3fc) + * + * @param dir + * the direction to orient towards + * @param up + * the up vector + * @return this + */ + public Matrix4x3f rotateTowards(Vector3fc dir, Vector3fc up) { + return rotateTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ). + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(float, float, float, float, float, float) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix4x3f().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invert()) + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + return rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ) + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * In order to set the matrix to a rotation transformation without post-multiplying it, + * use {@link #rotationTowards(float, float, float, float, float, float) rotationTowards()}. + *

+ * This method is equivalent to calling: mul(new Matrix4x3f().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invert(), dest) + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4x3f dest) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float ndirX = dirX * invDirLength; + float ndirY = dirY * invDirLength; + float ndirZ = dirZ * invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = ndirY * leftZ - ndirZ * leftY; + float upnY = ndirZ * leftX - ndirX * leftZ; + float upnZ = ndirX * leftY - ndirY * leftX; + float rm00 = leftX; + float rm01 = leftY; + float rm02 = leftZ; + float rm10 = upnX; + float rm11 = upnY; + float rm12 = upnZ; + float rm20 = ndirX; + float rm21 = ndirY; + float rm22 = ndirZ; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + float nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02; + float nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02; + float nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02; + float nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12; + float nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12; + float nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12; + dest.m20 = m00 * rm20 + m10 * rm21 + m20 * rm22; + dest.m21 = m01 * rm20 + m11 * rm21 + m21 * rm22; + dest.m22 = m02 * rm20 + m12 * rm21 + m22 * rm22; + dest.m00 = nm00; + dest.m01 = nm01; + dest.m02 = nm02; + dest.m10 = nm10; + dest.m11 = nm11; + dest.m12 = nm12; + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with dir. + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(float, float, float, float, float, float) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAt(new Vector3f(), new Vector3f(dir).negate(), up).invert() + * + * @see #rotationTowards(Vector3fc, Vector3fc) + * @see #rotateTowards(float, float, float, float, float, float) + * + * @param dir + * the direction to orient the local -z axis towards + * @param up + * the up vector + * @return this + */ + public Matrix4x3f rotationTowards(Vector3fc dir, Vector3fc up) { + return rotationTowards(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that aligns the local -z axis with (dirX, dirY, dirZ). + *

+ * In order to apply the rotation transformation to a previous existing transformation, + * use {@link #rotateTowards(float, float, float, float, float, float) rotateTowards}. + *

+ * This method is equivalent to calling: setLookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invert() + * + * @see #rotateTowards(Vector3fc, Vector3fc) + * @see #rotationTowards(float, float, float, float, float, float) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f rotationTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float ndirX = dirX * invDirLength; + float ndirY = dirY * invDirLength; + float ndirZ = dirZ * invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = ndirY * leftZ - ndirZ * leftY; + float upnY = ndirZ * leftX - ndirX * leftZ; + float upnZ = ndirX * leftY - ndirY * leftX; + this.m00 = leftX; + this.m01 = leftY; + this.m02 = leftZ; + this.m10 = upnX; + this.m11 = upnY; + this.m12 = upnZ; + this.m20 = ndirX; + this.m21 = ndirY; + this.m22 = ndirZ; + this.m30 = 0.0f; + this.m31 = 0.0f; + this.m32 = 0.0f; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that translates to the given pos and aligns the local -z + * axis with dir. + *

+ * This method is equivalent to calling: translation(pos).rotateTowards(dir, up) + * + * @see #translation(Vector3fc) + * @see #rotateTowards(Vector3fc, Vector3fc) + * + * @param pos + * the position to translate to + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @return this + */ + public Matrix4x3f translationRotateTowards(Vector3fc pos, Vector3fc dir, Vector3fc up) { + return translationRotateTowards(pos.x(), pos.y(), pos.z(), dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z()); + } + + /** + * Set this matrix to a model transformation for a right-handed coordinate system, + * that translates to the given (posX, posY, posZ) and aligns the local -z + * axis with (dirX, dirY, dirZ). + *

+ * This method is equivalent to calling: translation(posX, posY, posZ).rotateTowards(dirX, dirY, dirZ, upX, upY, upZ) + * + * @see #translation(float, float, float) + * @see #rotateTowards(float, float, float, float, float, float) + * + * @param posX + * the x-coordinate of the position to translate to + * @param posY + * the y-coordinate of the position to translate to + * @param posZ + * the z-coordinate of the position to translate to + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Matrix4x3f translationRotateTowards(float posX, float posY, float posZ, float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float ndirX = dirX * invDirLength; + float ndirY = dirY * invDirLength; + float ndirZ = dirZ * invDirLength; + // left = up x direction + float leftX, leftY, leftZ; + leftX = upY * ndirZ - upZ * ndirY; + leftY = upZ * ndirX - upX * ndirZ; + leftZ = upX * ndirY - upY * ndirX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = ndirY * leftZ - ndirZ * leftY; + float upnY = ndirZ * leftX - ndirX * leftZ; + float upnZ = ndirX * leftY - ndirY * leftX; + this.m00 = leftX; + this.m01 = leftY; + this.m02 = leftZ; + this.m10 = upnX; + this.m11 = upnY; + this.m12 = upnZ; + this.m20 = ndirX; + this.m21 = ndirY; + this.m22 = ndirZ; + this.m30 = posX; + this.m31 = posY; + this.m32 = posZ; + properties = PROPERTY_ORTHONORMAL; + return this; + } + + public Vector3f getEulerAnglesZYX(Vector3f dest) { + dest.x = Math.atan2(m12, m22); + dest.y = Math.atan2(-m02, Math.sqrt(1.0f - m02 * m02)); + dest.z = Math.atan2(m01, m00); + return dest; + } + + public Vector3f getEulerAnglesXYZ(Vector3f dest) { + dest.x = Math.atan2(-m21, m22); + dest.y = Math.atan2(m20, Math.sqrt(1.0f - m20 * m20)); + dest.z = Math.atan2(-m10, m00); + return dest; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @return this + */ + public Matrix4x3f obliqueZ(float a, float b) { + this.m20 = m00 * a + m10 * b + m20; + this.m21 = m01 * a + m11 * b + m21; + this.m22 = m02 * a + m12 * b + m22; + this.properties = 0; + return this; + } + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + public Matrix4x3f obliqueZ(float a, float b, Matrix4x3f dest) { + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m00 * a + m10 * b + m20; + dest.m21 = m01 * a + m11 * b + m21; + dest.m22 = m02 * a + m12 * b + m22; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.properties = 0; + return dest; + } + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3f)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3f)}) and the + * given vector up. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from + * {@link #setLookAt(Vector3fc, Vector3fc, Vector3fc)} called with the current + * local origin of this matrix (as obtained by {@link #origin(Vector3f)}), the sum of this position and the + * negated local Z axis as well as the given vector up. + * + * @param up + * the up vector + * @return this + */ + public Matrix4x3f withLookAtUp(Vector3fc up) { + return withLookAtUp(up.x(), up.y(), up.z(), this); + } + + public Matrix4x3f withLookAtUp(Vector3fc up, Matrix4x3f dest) { + return withLookAtUp(up.x(), up.y(), up.z()); + } + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3f)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3f)}) and the + * given vector (upX, upY, upZ). + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from + * {@link #setLookAt(float, float, float, float, float, float, float, float, float)} called with the current + * local origin of this matrix (as obtained by {@link #origin(Vector3f)}), the sum of this position and the + * negated local Z axis as well as the given vector (upX, upY, upZ). + * + * @param upX + * the x coordinate of the up vector + * @param upY + * the y coordinate of the up vector + * @param upZ + * the z coordinate of the up vector + * @return this + */ + public Matrix4x3f withLookAtUp(float upX, float upY, float upZ) { + return withLookAtUp(upX, upY, upZ, this); + } + + public Matrix4x3f withLookAtUp(float upX, float upY, float upZ, Matrix4x3f dest) { + float y = (upY * m21 - upZ * m11) * m02 + + (upZ * m01 - upX * m21) * m12 + + (upX * m11 - upY * m01) * m22; + float x = upX * m01 + upY * m11 + upZ * m21; + if ((properties & PROPERTY_ORTHONORMAL) == 0) + x *= Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21); + float invsqrt = Math.invsqrt(y * y + x * x); + float c = x * invsqrt, s = y * invsqrt; + float nm00 = c * m00 - s * m01, nm10 = c * m10 - s * m11, nm20 = c * m20 - s * m21, nm31 = s * m30 + c * m31; + float nm01 = s * m00 + c * m01, nm11 = s * m10 + c * m11, nm21 = s * m20 + c * m21, nm30 = c * m30 - s * m31; + dest + ._m00(nm00)._m10(nm10)._m20(nm20)._m30(nm30) + ._m01(nm01)._m11(nm11)._m21(nm21)._m31(nm31); + if (dest != this) { + dest + ._m02(m02)._m12(m12)._m22(m22)._m32(m32); + } + dest.properties = properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION); + return dest; + } + /** + * Multiply this by the matrix + *

+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapXZY() { + return mapXZY(this); + } + public Matrix4x3f mapXZY(Matrix4x3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m20)._m11(m21)._m12(m22)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapXZnY() { + return mapXZnY(this); + } + public Matrix4x3f mapXZnY(Matrix4x3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m20)._m11(m21)._m12(m22)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapXnYnZ() { + return mapXnYnZ(this); + } + public Matrix4x3f mapXnYnZ(Matrix4x3f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapXnZY() { + return mapXnZY(this); + } + public Matrix4x3f mapXnZY(Matrix4x3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapXnZnY() { + return mapXnZnY(this); + } + public Matrix4x3f mapXnZnY(Matrix4x3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapYXZ() { + return mapYXZ(this); + } + public Matrix4x3f mapYXZ(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m00)._m11(m01)._m12(m02)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapYXnZ() { + return mapYXnZ(this); + } + public Matrix4x3f mapYXnZ(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m00)._m11(m01)._m12(m02)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapYZX() { + return mapYZX(this); + } + public Matrix4x3f mapYZX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m20)._m11(m21)._m12(m22)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapYZnX() { + return mapYZnX(this); + } + public Matrix4x3f mapYZnX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(m20)._m11(m21)._m12(m22)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapYnXZ() { + return mapYnXZ(this); + } + public Matrix4x3f mapYnXZ(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapYnXnZ() { + return mapYnXnZ(this); + } + public Matrix4x3f mapYnXnZ(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapYnZX() { + return mapYnZX(this); + } + public Matrix4x3f mapYnZX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapYnZnX() { + return mapYnZnX(this); + } + public Matrix4x3f mapYnZnX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m10)._m01(m11)._m02(m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 0 0 1 0
+     * 1 0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapZXY() { + return mapZXY(this); + } + public Matrix4x3f mapZXY(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m00)._m11(m01)._m12(m02)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapZXnY() { + return mapZXnY(this); + } + public Matrix4x3f mapZXnY(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m00)._m11(m01)._m12(m02)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 0 1 0 0
+     * 1 0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapZYX() { + return mapZYX(this); + } + public Matrix4x3f mapZYX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m10)._m11(m11)._m12(m12)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 1 0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapZYnX() { + return mapZYnX(this); + } + public Matrix4x3f mapZYnX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(m10)._m11(m11)._m12(m12)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 1  0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapZnXY() { + return mapZnXY(this); + } + public Matrix4x3f mapZnXY(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapZnXnY() { + return mapZnXnY(this); + } + public Matrix4x3f mapZnXnY(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapZnYX() { + return mapZnYX(this); + } + public Matrix4x3f mapZnYX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapZnYnX() { + return mapZnYnX(this); + } + public Matrix4x3f mapZnYnX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(m20)._m01(m21)._m02(m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnXYnZ() { + return mapnXYnZ(this); + } + public Matrix4x3f mapnXYnZ(Matrix4x3f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m10)._m11(m11)._m12(m12)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnXZY() { + return mapnXZY(this); + } + public Matrix4x3f mapnXZY(Matrix4x3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m20)._m11(m21)._m12(m22)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnXZnY() { + return mapnXZnY(this); + } + public Matrix4x3f mapnXZnY(Matrix4x3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m20)._m11(m21)._m12(m22)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnXnYZ() { + return mapnXnYZ(this); + } + public Matrix4x3f mapnXnYZ(Matrix4x3f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnXnYnZ() { + return mapnXnYnZ(this); + } + public Matrix4x3f mapnXnYnZ(Matrix4x3f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0 -1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnXnZY() { + return mapnXnZY(this); + } + public Matrix4x3f mapnXnZY(Matrix4x3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnXnZnY() { + return mapnXnZnY(this); + } + public Matrix4x3f mapnXnZnY(Matrix4x3f dest) { + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnYXZ() { + return mapnYXZ(this); + } + public Matrix4x3f mapnYXZ(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m00)._m11(m01)._m12(m02)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnYXnZ() { + return mapnYXnZ(this); + } + public Matrix4x3f mapnYXnZ(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m00)._m11(m01)._m12(m02)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnYZX() { + return mapnYZX(this); + } + public Matrix4x3f mapnYZX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m20)._m11(m21)._m12(m22)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnYZnX() { + return mapnYZnX(this); + } + public Matrix4x3f mapnYZnX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(m20)._m11(m21)._m12(m22)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnYnXZ() { + return mapnYnXZ(this); + } + public Matrix4x3f mapnYnXZ(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnYnXnZ() { + return mapnYnXnZ(this); + } + public Matrix4x3f mapnYnXnZ(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0 -1 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnYnZX() { + return mapnYnZX(this); + } + public Matrix4x3f mapnYnZX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0 -1  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnYnZnX() { + return mapnYnZnX(this); + } + public Matrix4x3f mapnYnZnX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m10)._m01(-m11)._m02(-m12)._m10(-m20)._m11(-m21)._m12(-m22)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     *  0 0 1 0
+     * -1 0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnZXY() { + return mapnZXY(this); + } + public Matrix4x3f mapnZXY(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m00)._m11(m01)._m12(m02)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     *  0 0 -1 0
+     * -1 0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnZXnY() { + return mapnZXnY(this); + } + public Matrix4x3f mapnZXnY(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m00)._m11(m01)._m12(m02)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     *  0 1 0 0
+     * -1 0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnZYX() { + return mapnZYX(this); + } + public Matrix4x3f mapnZYX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m10)._m11(m11)._m12(m12)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     *  0 1  0 0
+     * -1 0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnZYnX() { + return mapnZYnX(this); + } + public Matrix4x3f mapnZYnX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(m10)._m11(m11)._m12(m12)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     *  0  0 1 0
+     * -1  0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnZnXY() { + return mapnZnXY(this); + } + public Matrix4x3f mapnZnXY(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(m10)._m21(m11)._m22(m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * -1  0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnZnXnY() { + return mapnZnXnY(this); + } + public Matrix4x3f mapnZnXnY(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + float m10 = this.m10, m11 = this.m11, m12 = this.m12; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m00)._m11(-m01)._m12(-m02)._m20(-m10)._m21(-m11)._m22(-m12)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     *  0 -1 0 0
+     * -1  0 0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnZnYX() { + return mapnZnYX(this); + } + public Matrix4x3f mapnZnYX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m00)._m21(m01)._m22(m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * -1  0  0 0
+     * 
+ * + * @return this + */ + public Matrix4x3f mapnZnYnX() { + return mapnZnYnX(this); + } + public Matrix4x3f mapnZnYnX(Matrix4x3f dest) { + float m00 = this.m00, m01 = this.m01, m02 = this.m02; + return dest._m00(-m20)._m01(-m21)._m02(-m22)._m10(-m10)._m11(-m11)._m12(-m12)._m20(-m00)._m21(-m01)._m22(-m02)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f negateX() { + return _m00(-m00)._m01(-m01)._m02(-m02)._properties(properties & PROPERTY_ORTHONORMAL); + } + public Matrix4x3f negateX(Matrix4x3f dest) { + return dest._m00(-m00)._m01(-m01)._m02(-m02)._m10(m10)._m11(m11)._m12(m12)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f negateY() { + return _m10(-m10)._m11(-m11)._m12(-m12)._properties(properties & PROPERTY_ORTHONORMAL); + } + public Matrix4x3f negateY(Matrix4x3f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(-m10)._m11(-m11)._m12(-m12)._m20(m20)._m21(m21)._m22(m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 
+ * + * @return this + */ + public Matrix4x3f negateZ() { + return _m20(-m20)._m21(-m21)._m22(-m22)._properties(properties & PROPERTY_ORTHONORMAL); + } + public Matrix4x3f negateZ(Matrix4x3f dest) { + return dest._m00(m00)._m01(m01)._m02(m02)._m10(m10)._m11(m11)._m12(m12)._m20(-m20)._m21(-m21)._m22(-m22)._m30(m30)._m31(m31)._m32(m32)._properties(properties & PROPERTY_ORTHONORMAL); + } + + public boolean isFinite() { + return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && + Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && + Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22) && + Math.isFinite(m30) && Math.isFinite(m31) && Math.isFinite(m32); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3fStack.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3fStack.java new file mode 100644 index 000000000..dff3e5a5b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3fStack.java @@ -0,0 +1,186 @@ +/* + * The MIT License + * + * Copyright (c) 2018-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A stack of many {@link Matrix4x3f} instances. This resembles the matrix stack known from legacy OpenGL. + *

+ * This {@link Matrix4x3fStack} class inherits from {@link Matrix4x3f}, so the current/top matrix is always the + * {@link Matrix4x3fStack}/{@link Matrix4x3f} itself. This affects all operations in {@link Matrix4x3f} that take + * another {@link Matrix4x3f} as parameter. If a {@link Matrix4x3fStack} is used as argument to those methods, the + * effective argument will always be the current matrix of the matrix stack. + * + * @author Kai Burjack + */ +public class Matrix4x3fStack extends Matrix4x3f { + + private static final long serialVersionUID = 1L; + + /** + * The matrix stack as a non-growable array. The size of the stack must be specified in the {@link #Matrix4x3fStack(int) constructor}. + */ + private Matrix4x3f[] mats; + + /** + * The index of the "current" matrix within {@link #mats}. + */ + private int curr; + + /** + * Create a new {@link Matrix4x3fStack} of the given size. + *

+ * Initially the stack pointer is at zero and the current matrix is set to identity. + * + * @param stackSize + * the size of the stack. This must be at least 1, in which case the {@link Matrix4x3fStack} simply only consists of this + * {@link Matrix4x3f} + */ + public Matrix4x3fStack(int stackSize) { + if (stackSize < 1) { + throw new IllegalArgumentException("stackSize must be >= 1"); //$NON-NLS-1$ + } + mats = new Matrix4x3f[stackSize - 1]; + // Allocate all matrices up front to keep the promise of being "allocation-free" + for (int i = 0; i < mats.length; i++) { + mats[i] = new Matrix4x3f(); + } + } + + /** + * Do not invoke manually! Only meant for serialization. + *

+ * Invoking this constructor from client code will result in an inconsistent state of the + * created {@link Matrix4x3fStack} instance. + */ + public Matrix4x3fStack() { + /* Empty! */ + } + + /** + * Set the stack pointer to zero and set the current/bottom matrix to {@link #identity() identity}. + * + * @return this + */ + public Matrix4x3fStack clear() { + curr = 0; + identity(); + return this; + } + + /** + * Increment the stack pointer by one and set the values of the new current matrix to the one directly below it. + * + * @return this + */ + public Matrix4x3fStack pushMatrix() { + if (curr == mats.length) { + throw new IllegalStateException("max stack size of " + (curr + 1) + " reached"); //$NON-NLS-1$ //$NON-NLS-2$ + } + mats[curr++].set(this); + return this; + } + + /** + * Decrement the stack pointer by one. + *

+ * This will effectively dispose of the current matrix. + * + * @return this + */ + public Matrix4x3fStack popMatrix() { + if (curr == 0) { + throw new IllegalStateException("already at the bottom of the stack"); //$NON-NLS-1$ + } + set(mats[--curr]); + return this; + } + + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + curr; + for (int i = 0; i < curr; i++) { + result = prime * result + mats[i].hashCode(); + } + return result; + } + + /* + * Contract between Matrix4x3f and Matrix4x3fStack: + * + * - Matrix4x3f.equals(Matrix4x3fStack) is true iff all the 12 matrix elements are equal + * - Matrix4x3fStack.equals(Matrix4x3f) is true iff all the 12 matrix elements are equal + * - Matrix4x3fStack.equals(Matrix4x3fStack) is true iff all 12 matrix elements are equal AND the matrix arrays as well as the stack pointer are equal + * - everything else is inequal + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (obj instanceof Matrix4x3fStack) { + Matrix4x3fStack other = (Matrix4x3fStack) obj; + if (curr != other.curr) + return false; + for (int i = 0; i < curr; i++) { + if (!mats[i].equals(other.mats[i])) + return false; + } + } + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(curr); + for (int i = 0; i < curr; i++) { + out.writeObject(mats[i]); + } + } + + public void readExternal(ObjectInput in) throws IOException { + super.readExternal(in); + curr = in.readInt(); + mats = new Matrix4x3fStack[curr]; + for (int i = 0; i < curr; i++) { + Matrix4x3f m = new Matrix4x3f(); + m.readExternal(in); + mats[i] = m; + } + } + + public Object clone() throws CloneNotSupportedException { + Matrix4x3fStack cloned = (Matrix4x3fStack) super.clone(); + Matrix4x3f[] clonedMats = new Matrix4x3f[mats.length]; + for (int i = 0; i < mats.length; i++) + clonedMats[i] = (Matrix4x3f) mats[i].clone(); + cloned.mats = clonedMats; + return cloned; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3fc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3fc.java new file mode 100644 index 000000000..d06253eb9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Matrix4x3fc.java @@ -0,0 +1,3544 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + + +/** + * Interface to a read-only view of a 4x3 matrix of single-precision floats. + * + * @author Kai Burjack + */ +public interface Matrix4x3fc { + + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation x=-1 when using the identity matrix. + */ + int PLANE_NX = 0; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation x=1 when using the identity matrix. + */ + int PLANE_PX = 1; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation y=-1 when using the identity matrix. + */ + int PLANE_NY = 2; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation y=1 when using the identity matrix. + */ + int PLANE_PY = 3; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation z=-1 when using the identity matrix. + */ + int PLANE_NZ = 4; + /** + * Argument to the first parameter of {@link #frustumPlane(int, Vector4f)} + * identifying the plane with equation z=1 when using the identity matrix. + */ + int PLANE_PZ = 5; + + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents the identity transformation. + */ + byte PROPERTY_IDENTITY = 1<<2; + /** + * Bit returned by {@link #properties()} to indicate that the matrix represents a pure translation transformation. + */ + byte PROPERTY_TRANSLATION = 1<<3; + /** + * Bit returned by {@link #properties()} to indicate that the left 3x3 submatrix represents an orthogonal + * matrix (i.e. orthonormal basis). + */ + byte PROPERTY_ORTHONORMAL = 1<<4; + + /** + * @return the properties of the matrix + */ + int properties(); + + /** + * Return the value of the matrix element at column 0 and row 0. + * + * @return the value of the matrix element + */ + float m00(); + + /** + * Return the value of the matrix element at column 0 and row 1. + * + * @return the value of the matrix element + */ + float m01(); + + /** + * Return the value of the matrix element at column 0 and row 2. + * + * @return the value of the matrix element + */ + float m02(); + + /** + * Return the value of the matrix element at column 1 and row 0. + * + * @return the value of the matrix element + */ + float m10(); + + /** + * Return the value of the matrix element at column 1 and row 1. + * + * @return the value of the matrix element + */ + float m11(); + + /** + * Return the value of the matrix element at column 1 and row 2. + * + * @return the value of the matrix element + */ + float m12(); + + /** + * Return the value of the matrix element at column 2 and row 0. + * + * @return the value of the matrix element + */ + float m20(); + + /** + * Return the value of the matrix element at column 2 and row 1. + * + * @return the value of the matrix element + */ + float m21(); + + /** + * Return the value of the matrix element at column 2 and row 2. + * + * @return the value of the matrix element + */ + float m22(); + + /** + * Return the value of the matrix element at column 3 and row 0. + * + * @return the value of the matrix element + */ + float m30(); + + /** + * Return the value of the matrix element at column 3 and row 1. + * + * @return the value of the matrix element + */ + float m31(); + + /** + * Return the value of the matrix element at column 3 and row 2. + * + * @return the value of the matrix element + */ + float m32(); + + /** + * Get the current values of this matrix and store them into the upper 4x3 submatrix of dest. + *

+ * The other elements of dest will not be modified. + * + * @see Matrix4f#set4x3(Matrix4x3fc) + * + * @param dest + * the destination matrix + * @return dest + */ + Matrix4f get(Matrix4f dest); + + /** + * Get the current values of this matrix and store them into the upper 4x3 submatrix of dest. + *

+ * The other elements of dest will not be modified. + * + * @see Matrix4d#set4x3(Matrix4x3fc) + * + * @param dest + * the destination matrix + * @return dest + */ + Matrix4d get(Matrix4d dest); + + /** + * Multiply this matrix by the supplied right matrix and store the result in dest. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4x3f mul(Matrix4x3fc right, Matrix4x3f dest); + + /** + * Multiply this matrix, which is assumed to only contain a translation, by the supplied right matrix and store the result in dest. + *

+ * This method assumes that this matrix only contains a translation. + *

+ * This method will not modify either the last row of this or the last row of right. + *

+ * If M is this matrix and R the right matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the right matrix will be applied first! + * + * @param right + * the right operand of the matrix multiplication + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4x3f mulTranslation(Matrix4x3fc right, Matrix4x3f dest); + + /** + * Multiply this orthographic projection matrix by the supplied view matrix + * and store the result in dest. + *

+ * If M is this matrix and V the view matrix, + * then the new matrix will be M * V. So when transforming a + * vector v with the new matrix by using M * V * v, the + * transformation of the view matrix will be applied first! + * + * @param view + * the matrix which to multiply this with + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4x3f mulOrtho(Matrix4x3fc view, Matrix4x3f dest); + + /** + * Multiply this by the 4x3 matrix with the column vectors (rm00, rm01, rm02), + * (rm10, rm11, rm12), (rm20, rm21, rm22) and (0, 0, 0) + * and store the result in dest. + *

+ * If M is this matrix and R the specified matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * transformation of the R matrix will be applied first! + * + * @param rm00 + * the value of the m00 element + * @param rm01 + * the value of the m01 element + * @param rm02 + * the value of the m02 element + * @param rm10 + * the value of the m10 element + * @param rm11 + * the value of the m11 element + * @param rm12 + * the value of the m12 element + * @param rm20 + * the value of the m20 element + * @param rm21 + * the value of the m21 element + * @param rm22 + * the value of the m22 element + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mul3x3(float rm00, float rm01, float rm02, float rm10, float rm11, float rm12, float rm20, float rm21, float rm22, Matrix4x3f dest); + + /** + * Component-wise add this and other + * by first multiplying each component of other by otherFactor, + * adding that to this and storing the final result in dest. + *

+ * The other components of dest will be set to the ones of this. + *

+ * The matrices this and other will not be changed. + * + * @param other + * the other matrix + * @param otherFactor + * the factor to multiply each of the other matrix's components + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f fma(Matrix4x3fc other, float otherFactor, Matrix4x3f dest); + + /** + * Component-wise add this and other and store the result in dest. + * + * @param other + * the other addend + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f add(Matrix4x3fc other, Matrix4x3f dest); + + /** + * Component-wise subtract subtrahend from this and store the result in dest. + * + * @param subtrahend + * the subtrahend + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f sub(Matrix4x3fc subtrahend, Matrix4x3f dest); + + /** + * Component-wise multiply this by other and store the result in dest. + * + * @param other + * the other matrix + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mulComponentWise(Matrix4x3fc other, Matrix4x3f dest); + + /** + * Return the determinant of this matrix. + * + * @return the determinant + */ + float determinant(); + + /** + * Invert this matrix and write the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f invert(Matrix4x3f dest); + + /** + * Invert this matrix and write the result as the top 4x3 matrix into dest + * and set all other values of dest to identity.. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4f invert(Matrix4f dest); + + /** + * Invert this orthographic projection matrix and store the result into the given dest. + *

+ * This method can be used to quickly obtain the inverse of an orthographic projection matrix. + * + * @param dest + * will hold the inverse of this + * @return dest + */ + Matrix4x3f invertOrtho(Matrix4x3f dest); + + /** + * Transpose only the left 3x3 submatrix of this matrix and store the result in dest. + *

+ * All other matrix elements are left unchanged. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f transpose3x3(Matrix4x3f dest); + + /** + * Transpose only the left 3x3 submatrix of this matrix and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f transpose3x3(Matrix3f dest); + + /** + * Get only the translation components (m30, m31, m32) of this matrix and store them in the given vector xyz. + * + * @param dest + * will hold the translation components of this matrix + * @return dest + */ + Vector3f getTranslation(Vector3f dest); + + /** + * Get the scaling factors of this matrix for the three base axes. + * + * @param dest + * will hold the scaling factors for x, y and z + * @return dest + */ + Vector3f getScale(Vector3f dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4x3f get(Matrix4x3f dest); + + /** + * Get the current values of this matrix and store them into + * dest. + * + * @param dest + * the destination matrix + * @return the passed in destination + */ + Matrix4x3d get(Matrix4x3d dest); + + /** + * Get the rotational component of this matrix and store the represented rotation + * into the given {@link AxisAngle4f}. + * + * @see AxisAngle4f#set(Matrix4x3fc) + * + * @param dest + * the destination {@link AxisAngle4f} + * @return the passed in destination + */ + AxisAngle4f getRotation(AxisAngle4f dest); + + /** + * Get the rotational component of this matrix and store the represented rotation + * into the given {@link AxisAngle4d}. + * + * @see AxisAngle4f#set(Matrix4x3fc) + * + * @param dest + * the destination {@link AxisAngle4d} + * @return the passed in destination + */ + AxisAngle4d getRotation(AxisAngle4d dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the first three column vectors of the left 3x3 submatrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaternionf#setFromUnnormalized(Matrix4x3fc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getUnnormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaternionf}. + *

+ * This method assumes that the first three column vectors of the left 3x3 submatrix are normalized. + * + * @see Quaternionf#setFromNormalized(Matrix4x3fc) + * + * @param dest + * the destination {@link Quaternionf} + * @return the passed in destination + */ + Quaternionf getNormalizedRotation(Quaternionf dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the first three column vectors of the left 3x3 submatrix are not normalized and + * thus allows to ignore any additional scaling factor that is applied to the matrix. + * + * @see Quaterniond#setFromUnnormalized(Matrix4x3fc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getUnnormalizedRotation(Quaterniond dest); + + /** + * Get the current values of this matrix and store the represented rotation + * into the given {@link Quaterniond}. + *

+ * This method assumes that the first three column vectors of the left 3x3 submatrix are normalized. + * + * @see Quaterniond#setFromNormalized(Matrix4x3fc) + * + * @param dest + * the destination {@link Quaterniond} + * @return the passed in destination + */ + Quaterniond getNormalizedRotation(Quaterniond dest); + + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this matrix in column-major order at the given off-heap address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this matrix + * @return this + */ + Matrix4x3fc getToAddress(long address); + + /** + * Store this matrix into the supplied float array in column-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get(float[] arr, int offset); + + /** + * Store this matrix into the supplied float array in column-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #get(float[], int)}. + * + * @see #get(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get(float[] arr); + + /** + * Store a 4x4 matrix in column-major order into the supplied array at the given offset, + * where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] get4x4(float[] arr, int offset); + + /** + * Store a 4x4 matrix in column-major order into the supplied array, + * where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * In order to specify an explicit offset into the array, use the method {@link #get4x4(float[], int)}. + * + * @see #get4x4(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] get4x4(float[] arr); + + /** + * Store a 4x4 matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}, where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get4x4(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get4x4(FloatBuffer buffer); + + /** + * Store a 4x4 matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index, where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get4x4(int index, FloatBuffer buffer); + + /** + * Store a 4x4 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}, where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get4x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get4x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get4x4(ByteBuffer buffer); + + /** + * Store a 4x4 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index, where the upper 4x3 submatrix is this and the last row is (0, 0, 0, 1). + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get4x4(int index, ByteBuffer buffer); + + /** + * Store the left 3x3 submatrix as 3x4 matrix in column-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}, with the m03, m13 and m23 components being zero. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #get3x4(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x4(int, FloatBuffer) + * + * @param buffer + * will receive the values of the left 3x3 submatrix as 3x4 matrix in column-major order at its current position + * @return the passed in buffer + */ + FloatBuffer get3x4(FloatBuffer buffer); + + /** + * Store the left 3x3 submatrix as 3x4 matrix in column-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index, with the m03, m13 and m23 components being zero. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of the left 3x3 submatrix as 3x4 matrix in column-major order + * @return the passed in buffer + */ + FloatBuffer get3x4(int index, FloatBuffer buffer); + + /** + * Store the left 3x3 submatrix as 3x4 matrix in column-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}, with the m03, m13 and m23 components being zero. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #get3x4(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get3x4(int, ByteBuffer) + * + * @param buffer + * will receive the values of the left 3x3 submatrix as 3x4 matrix in column-major order at its current position + * @return the passed in buffer + */ + ByteBuffer get3x4(ByteBuffer buffer); + + /** + * Store the left 3x3 submatrix as 3x4 matrix in column-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index, with the m03, m13 and m23 components being zero. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of the left 3x3 submatrix as 3x4 matrix in column-major order + * @return the passed in buffer + */ + ByteBuffer get3x4(int index, ByteBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the matrix is stored, use {@link #getTransposed(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, FloatBuffer) + * + * @param buffer + * will receive the values of this matrix in row-major order at its current position + * @return the passed in buffer + */ + FloatBuffer getTransposed(FloatBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this matrix in row-major order + * @return the passed in buffer + */ + FloatBuffer getTransposed(int index, FloatBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the matrix is stored, use {@link #getTransposed(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #getTransposed(int, ByteBuffer) + * + * @param buffer + * will receive the values of this matrix in row-major order at its current position + * @return the passed in buffer + */ + ByteBuffer getTransposed(ByteBuffer buffer); + + /** + * Store this matrix in row-major order into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this matrix in row-major order + * @return the passed in buffer + */ + ByteBuffer getTransposed(int index, ByteBuffer buffer); + + /** + * Store this matrix into the supplied float array in row-major order at the given offset. + * + * @param arr + * the array to write the matrix values into + * @param offset + * the offset into the array + * @return the passed in array + */ + float[] getTransposed(float[] arr, int offset); + + /** + * Store this matrix into the supplied float array in row-major order. + *

+ * In order to specify an explicit offset into the array, use the method {@link #getTransposed(float[], int)}. + * + * @see #getTransposed(float[], int) + * + * @param arr + * the array to write the matrix values into + * @return the passed in array + */ + float[] getTransposed(float[] arr); + + /** + * Transform/multiply the given vector by this matrix and store the result in that vector. + * + * @see Vector4f#mul(Matrix4x3fc) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector4f transform(Vector4f v); + + /** + * Transform/multiply the given vector by this matrix and store the result in dest. + * + * @see Vector4f#mul(Matrix4x3fc, Vector4f) + * + * @param v + * the vector to transform + * @param dest + * will contain the result + * @return dest + */ + Vector4f transform(Vector4fc v, Vector4f dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=1, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. + *

+ * In order to store the result in another vector, use {@link #transformPosition(Vector3fc, Vector3f)}. + * + * @see #transformPosition(Vector3fc, Vector3f) + * @see #transform(Vector4f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3f transformPosition(Vector3f v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=1, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 1.0, so it + * will represent a position/location in 3D-space rather than a direction. + *

+ * In order to store the result in the same vector, use {@link #transformPosition(Vector3f)}. + * + * @see #transformPosition(Vector3f) + * @see #transform(Vector4fc, Vector4f) + * + * @param v + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPosition(Vector3fc v, Vector3f dest); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in that vector. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in another vector, use {@link #transformDirection(Vector3fc, Vector3f)}. + * + * @see #transformDirection(Vector3fc, Vector3f) + * + * @param v + * the vector to transform and to hold the final result + * @return v + */ + Vector3f transformDirection(Vector3f v); + + /** + * Transform/multiply the given 3D-vector, as if it was a 4D-vector with w=0, by + * this matrix and store the result in dest. + *

+ * The given 3D-vector is treated as a 4D-vector with its w-component being 0.0, so it + * will represent a direction in 3D-space rather than a position. This method will therefore + * not take the translation part of the matrix into account. + *

+ * In order to store the result in the same vector, use {@link #transformDirection(Vector3f)}. + * + * @see #transformDirection(Vector3f) + * + * @param v + * the vector to transform and to hold the final result + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformDirection(Vector3fc v, Vector3f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given xyz.x, + * xyz.y and xyz.z factors, respectively and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param xyz + * the factors of the x, y and z component, respectively + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f scale(Vector3fc xyz, Matrix4x3f dest); + + /** + * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * Individual scaling of all three axes can be applied using {@link #scale(float, float, float, Matrix4x3f)}. + * + * @see #scale(float, float, float, Matrix4x3f) + * + * @param xyz + * the factor for all components + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f scale(float xyz, Matrix4x3f dest); + + /** + * Apply scaling to this matrix by by scaling the X axis by x and the Y axis by y + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f scaleXY(float x, float y, Matrix4x3f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given sx, + * sy and sz factors while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).scale(sx, sy, sz).translate(-ox, -oy, -oz) + * + * @param sx + * the scaling factor of the x component + * @param sy + * the scaling factor of the y component + * @param sz + * the scaling factor of the z component + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f scaleAround(float sx, float sy, float sz, float ox, float oy, float oz, Matrix4x3f dest); + + /** + * Apply scaling to this matrix by scaling all three base axes by the given factor + * while using (ox, oy, oz) as the scaling origin, + * and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * scaling will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).scale(factor).translate(-ox, -oy, -oz) + * + * @param factor + * the scaling factor for all three axes + * @param ox + * the x coordinate of the scaling origin + * @param oy + * the y coordinate of the scaling origin + * @param oz + * the z coordinate of the scaling origin + * @param dest + * will hold the result + * @return this + */ + Matrix4x3f scaleAround(float factor, float ox, float oy, float oz, Matrix4x3f dest); + + /** + * Apply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v + * , the scaling will be applied first! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f scale(float x, float y, float z, Matrix4x3f dest); + + /** + * Pre-multiply scaling to this matrix by scaling the base axes by the given x, + * y and z factors and store the result in dest. + *

+ * If M is this matrix and S the scaling matrix, + * then the new matrix will be S * M. So when transforming a + * vector v with the new matrix by using S * M * v + * , the scaling will be applied last! + * + * @param x + * the factor of the x component + * @param y + * the factor of the y component + * @param z + * the factor of the z component + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f scaleLocal(float x, float y, float z, Matrix4x3f dest); + + /** + * Apply rotation about the X axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateX(float ang, Matrix4x3f dest); + + /** + * Apply rotation about the Y axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateY(float ang, Matrix4x3f dest); + + /** + * Apply rotation about the Z axis to this matrix by rotating the given amount of radians + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateZ(float ang, Matrix4x3f dest); + + /** + * Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateX(angleX, dest).rotateY(angleY).rotateZ(angleZ) + * + * @param angleX + * the angle to rotate about X + * @param angleY + * the angle to rotate about Y + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateXYZ(float angleX, float angleY, float angleZ, Matrix4x3f dest); + + /** + * Apply rotation of angleZ radians about the Z axis, followed by a rotation of angleY radians about the Y axis and + * followed by a rotation of angleX radians about the X axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateZ(angleZ, dest).rotateY(angleY).rotateX(angleX) + * + * @param angleZ + * the angle to rotate about Z + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateZYX(float angleZ, float angleY, float angleX, Matrix4x3f dest); + + /** + * Apply rotation of angleY radians about the Y axis, followed by a rotation of angleX radians about the X axis and + * followed by a rotation of angleZ radians about the Z axis and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * This method is equivalent to calling: rotateY(angleY, dest).rotateX(angleX).rotateZ(angleZ) + * + * @param angleY + * the angle to rotate about Y + * @param angleX + * the angle to rotate about X + * @param angleZ + * the angle to rotate about Z + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateYXZ(float angleY, float angleX, float angleZ, Matrix4x3f dest); + + /** + * Apply rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotate(float ang, float x, float y, float z, Matrix4x3f dest); + + /** + * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateTranslation(float ang, float x, float y, float z, Matrix4x3f dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix while using (ox, oy, oz) as the rotation origin, + * and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * This method is equivalent to calling: translate(ox, oy, oz, dest).rotate(quat).translate(-ox, -oy, -oz) + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param ox + * the x coordinate of the rotation origin + * @param oy + * the y coordinate of the rotation origin + * @param oz + * the z coordinate of the rotation origin + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateAround(Quaternionfc quat, float ox, float oy, float oz, Matrix4x3f dest); + + /** + * Pre-multiply a rotation to this matrix by rotating the given amount of radians + * about the specified (x, y, z) axis and store the result in dest. + *

+ * The axis described by the three components needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and R the rotation matrix, + * then the new matrix will be R * M. So when transforming a + * vector v with the new matrix by using R * M * v, the + * rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param ang + * the angle in radians + * @param x + * the x component of the axis + * @param y + * the y component of the axis + * @param z + * the z component of the axis + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateLocal(float ang, float x, float y, float z, Matrix4x3f dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f translate(Vector3fc offset, Matrix4x3f dest); + + /** + * Apply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be M * T. So when + * transforming a vector v with the new matrix by using + * M * T * v, the translation will be applied first! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f translate(float x, float y, float z, Matrix4x3f dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param offset + * the number of units in x, y and z by which to translate + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f translateLocal(Vector3fc offset, Matrix4x3f dest); + + /** + * Pre-multiply a translation to this matrix by translating by the given number of + * units in x, y and z and store the result in dest. + *

+ * If M is this matrix and T the translation + * matrix, then the new matrix will be T * M. So when + * transforming a vector v with the new matrix by using + * T * M * v, the translation will be applied last! + * + * @param x + * the offset to translate in x + * @param y + * the offset to translate in y + * @param z + * the offset to translate in z + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f translateLocal(float x, float y, float z, Matrix4x3f dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f ortho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4x3f dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f ortho(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4x3f dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne, Matrix4x3f dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordiante system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f orthoLH(float left, float right, float bottom, float top, float zNear, float zFar, Matrix4x3f dest); + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, boolean, Matrix4x3f) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4x3f orthoSymmetric(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4x3f dest); + + /** + * Apply a symmetric orthographic projection transformation for a right-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, Matrix4x3f) ortho()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f orthoSymmetric(float width, float height, float zNear, float zFar, Matrix4x3f dest); + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using the given NDC z range to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, boolean, Matrix4x3f) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @param zZeroToOne + * whether to use Vulkan's and Direct3D's NDC z range of [0..+1] when true + * or whether to use OpenGL's NDC z range of [-1..+1] when false + * @return dest + */ + Matrix4x3f orthoSymmetricLH(float width, float height, float zNear, float zFar, boolean zZeroToOne, Matrix4x3f dest); + + /** + * Apply a symmetric orthographic projection transformation for a left-handed coordinate system + * using OpenGL's NDC z range of [-1..+1] to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, Matrix4x3f) orthoLH()} with + * left=-width/2, right=+width/2, bottom=-height/2 and top=+height/2. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @param width + * the distance between the right and left frustum edges + * @param height + * the distance between the top and bottom frustum edges + * @param zNear + * near clipping plane distance + * @param zFar + * far clipping plane distance + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f orthoSymmetricLH(float width, float height, float zNear, float zFar, Matrix4x3f dest); + + /** + * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix + * and store the result in dest. + *

+ * This method is equivalent to calling {@link #ortho(float, float, float, float, float, float, Matrix4x3f) ortho()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @see #ortho(float, float, float, float, float, float, Matrix4x3f) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f ortho2D(float left, float right, float bottom, float top, Matrix4x3f dest); + + /** + * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in dest. + *

+ * This method is equivalent to calling {@link #orthoLH(float, float, float, float, float, float, Matrix4x3f) orthoLH()} with + * zNear=-1 and zFar=+1. + *

+ * If M is this matrix and O the orthographic projection matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * orthographic projection transformation will be applied first! + *

+ * Reference: http://www.songho.ca + * + * @see #orthoLH(float, float, float, float, float, float, Matrix4x3f) + * + * @param left + * the distance from the center to the left frustum edge + * @param right + * the distance from the center to the right frustum edge + * @param bottom + * the distance from the center to the bottom frustum edge + * @param top + * the distance from the center to the top frustum edge + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f ortho2DLH(float left, float right, float bottom, float top, Matrix4x3f dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(Vector3fc, Vector3fc, Vector3fc, Matrix4x3f) lookAt} + * with eye = (0, 0, 0) and center = dir. + * + * @see #lookAlong(float, float, float, float, float, float, Matrix4x3f) + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc, Matrix4x3f) + * + * @param dir + * the direction in space to look along + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f lookAlong(Vector3fc dir, Vector3fc up, Matrix4x3f dest); + + /** + * Apply a rotation transformation to this matrix to make -z point along dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookalong rotation matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, the + * lookalong rotation transformation will be applied first! + *

+ * This is equivalent to calling + * {@link #lookAt(float, float, float, float, float, float, float, float, float, Matrix4x3f) lookAt()} + * with eye = (0, 0, 0) and center = dir. + * + * @see #lookAt(float, float, float, float, float, float, float, float, float, Matrix4x3f) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4x3f dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAt(float, float, float, float, float, float, float, float, float, Matrix4x3f) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f lookAt(Vector3fc eye, Vector3fc center, Vector3fc up, Matrix4x3f dest); + + /** + * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, + * that aligns -z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAt(Vector3fc, Vector3fc, Vector3fc, Matrix4x3f) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f lookAt(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ, Matrix4x3f dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAtLH(float, float, float, float, float, float, float, float, float, Matrix4x3f) + * + * @param eye + * the position of the camera + * @param center + * the point in space to look at + * @param up + * the direction of 'up' + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f lookAtLH(Vector3fc eye, Vector3fc center, Vector3fc up, Matrix4x3f dest); + + /** + * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, + * that aligns +z with center - eye and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + * + * @see #lookAtLH(Vector3fc, Vector3fc, Vector3fc, Matrix4x3f) + * + * @param eyeX + * the x-coordinate of the eye/camera location + * @param eyeY + * the y-coordinate of the eye/camera location + * @param eyeZ + * the z-coordinate of the eye/camera location + * @param centerX + * the x-coordinate of the point to look at + * @param centerY + * the y-coordinate of the point to look at + * @param centerZ + * the z-coordinate of the point to look at + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f lookAtLH(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ, Matrix4x3f dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotate(Quaternionfc quat, Matrix4x3f dest); + + /** + * Apply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix, which is assumed to only contain a translation, and store + * the result in dest. + *

+ * This method assumes this to only contain a translation. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be M * Q. So when transforming a + * vector v with the new matrix by using M * Q * v, + * the quaternion rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateTranslation(Quaternionfc quat, Matrix4x3f dest); + + /** + * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaternionfc} to this matrix and store + * the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and Q the rotation matrix obtained from the given quaternion, + * then the new matrix will be Q * M. So when transforming a + * vector v with the new matrix by using Q * M * v, + * the quaternion rotation will be applied last! + *

+ * Reference: http://en.wikipedia.org + * + * @param quat + * the {@link Quaternionfc} + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateLocal(Quaternionfc quat, Matrix4x3f dest); + + /** + * Apply a rotation transformation, rotating about the given {@link AxisAngle4f} and store the result in dest. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given {@link AxisAngle4f}, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the {@link AxisAngle4f} rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float, Matrix4x3f) + * + * @param axisAngle + * the {@link AxisAngle4f} (needs to be {@link AxisAngle4f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotate(AxisAngle4f axisAngle, Matrix4x3f dest); + + /** + * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in dest. + *

+ * The axis described by the axis vector needs to be a unit vector. + *

+ * When used with a right-handed coordinate system, the produced rotation will rotate a vector + * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin. + * When used with a left-handed coordinate system, the rotation is clockwise. + *

+ * If M is this matrix and A the rotation matrix obtained from the given axis-angle, + * then the new matrix will be M * A. So when transforming a + * vector v with the new matrix by using M * A * v, + * the axis-angle rotation will be applied first! + *

+ * Reference: http://en.wikipedia.org + * + * @see #rotate(float, float, float, float, Matrix4x3f) + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis (needs to be {@link Vector3f#normalize() normalized}) + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotate(float angle, Vector3fc axis, Matrix4x3f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the equation x*a + y*b + z*c + d = 0 and store the result in dest. + *

+ * The vector (a, b, c) must be a unit vector. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + *

+ * Reference: msdn.microsoft.com + * + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f reflect(float a, float b, float c, float d, Matrix4x3f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param nx + * the x-coordinate of the plane normal + * @param ny + * the y-coordinate of the plane normal + * @param nz + * the z-coordinate of the plane normal + * @param px + * the x-coordinate of a point on the plane + * @param py + * the y-coordinate of a point on the plane + * @param pz + * the z-coordinate of a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f reflect(float nx, float ny, float nz, float px, float py, float pz, Matrix4x3f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about a plane + * specified via the plane orientation and a point on the plane, and store the result in dest. + *

+ * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene. + * It is assumed that the default mirror plane's normal is (0, 0, 1). So, if the given {@link Quaternionfc} is + * the identity (does not apply any additional rotation), the reflection plane will be z=0, offset by the given point. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param orientation + * the plane orientation relative to an implied normal vector of (0, 0, 1) + * @param point + * a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f reflect(Quaternionfc orientation, Vector3fc point, Matrix4x3f dest); + + /** + * Apply a mirror/reflection transformation to this matrix that reflects about the given plane + * specified via the plane normal and a point on the plane, and store the result in dest. + *

+ * If M is this matrix and R the reflection matrix, + * then the new matrix will be M * R. So when transforming a + * vector v with the new matrix by using M * R * v, the + * reflection will be applied first! + * + * @param normal + * the plane normal + * @param point + * a point on the plane + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f reflect(Vector3fc normal, Vector3fc point, Matrix4x3f dest); + + /** + * Get the row at the given row index, starting with 0. + * + * @param row + * the row index in [0..2] + * @param dest + * will hold the row components + * @return the passed in destination + * @throws IndexOutOfBoundsException if row is not in [0..2] + */ + Vector4f getRow(int row, Vector4f dest) throws IndexOutOfBoundsException; + + /** + * Get the column at the given column index, starting with 0. + * + * @param column + * the column index in [0..2] + * @param dest + * will hold the column components + * @return the passed in destination + * @throws IndexOutOfBoundsException if column is not in [0..2] + */ + Vector3f getColumn(int column, Vector3f dest) throws IndexOutOfBoundsException; + + /** + * Compute a normal matrix from the left 3x3 submatrix of this + * and store it into the left 3x3 submatrix of dest. + * All other values of dest will be set to identity. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f normal(Matrix4x3f dest); + + /** + * Compute a normal matrix from the left 3x3 submatrix of this and store it into dest. + *

+ * The normal matrix of m is the transpose of the inverse of m. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f normal(Matrix3f dest); + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this + * and store it into dest. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix3f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f cofactor3x3(Matrix3f dest); + + /** + * Compute the cofactor matrix of the left 3x3 submatrix of this + * and store it into dest. + * All other values of dest will be set to identity. + *

+ * The cofactor matrix can be used instead of {@link #normal(Matrix4x3f)} to transform normals + * when the orientation of the normals with respect to the surface should be preserved. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f cofactor3x3(Matrix4x3f dest); + + /** + * Normalize the left 3x3 submatrix of this matrix and store the result in dest. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f normalize3x3(Matrix4x3f dest); + + /** + * Normalize the left 3x3 submatrix of this matrix and store the result in dest. + *

+ * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit + * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself + * (i.e. had skewing). + * + * @param dest + * will hold the result + * @return dest + */ + Matrix3f normalize3x3(Matrix3f dest); + + /** + * Calculate a frustum plane of this matrix, which + * can be a projection matrix or a combined modelview-projection matrix, and store the result + * in the given dest. + *

+ * Generally, this method computes the frustum plane in the local frame of + * any coordinate system that existed before this + * transformation was applied to it in order to yield homogeneous clipping space. + *

+ * The plane normal, which is (a, b, c), is directed "inwards" of the frustum. + * Any plane/point test using a*x + b*y + c*z + d therefore will yield a result greater than zero + * if the point is within the frustum (i.e. at the positive side of the frustum plane). + *

+ * Reference: + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * + * @param which + * one of the six possible planes, given as numeric constants + * {@link #PLANE_NX}, {@link #PLANE_PX}, + * {@link #PLANE_NY}, {@link #PLANE_PY}, + * {@link #PLANE_NZ} and {@link #PLANE_PZ} + * @param dest + * will hold the computed plane equation. + * The plane equation will be normalized, meaning that (a, b, c) will be a unit vector + * @return dest + */ + Vector4f frustumPlane(int which, Vector4f dest); + + /** + * Obtain the direction of +Z before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +Z by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3f inv = new Matrix4x3f(this).invert();
+     * inv.transformDirection(dir.set(0, 0, 1)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveZ(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3f positiveZ(Vector3f dir); + + /** + * Obtain the direction of +Z before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +Z by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3f inv = new Matrix4x3f(this).transpose();
+     * inv.transformDirection(dir.set(0, 0, 1)).normalize();
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3f normalizedPositiveZ(Vector3f dir); + + /** + * Obtain the direction of +X before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3f inv = new Matrix4x3f(this).invert();
+     * inv.transformDirection(dir.set(1, 0, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveX(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3f positiveX(Vector3f dir); + + /** + * Obtain the direction of +X before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +X by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3f inv = new Matrix4x3f(this).transpose();
+     * inv.transformDirection(dir.set(1, 0, 0)).normalize();
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3f normalizedPositiveX(Vector3f dir); + + /** + * Obtain the direction of +Y before the transformation represented by this matrix is applied. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3f inv = new Matrix4x3f(this).invert();
+     * inv.transformDirection(dir.set(0, 1, 0)).normalize();
+     * 
+ * If this is already an orthogonal matrix, then consider using {@link #normalizedPositiveY(Vector3f)} instead. + *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3f positiveY(Vector3f dir); + + /** + * Obtain the direction of +Y before the transformation represented by this orthogonal matrix is applied. + * This method only produces correct results if this is an orthogonal matrix. + *

+ * This method uses the rotation component of the left 3x3 submatrix to obtain the direction + * that is transformed to +Y by this matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3f inv = new Matrix4x3f(this).transpose();
+     * inv.transformDirection(dir.set(0, 1, 0)).normalize();
+     * 
+ *

+ * Reference: http://www.euclideanspace.com + * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3f normalizedPositiveY(Vector3f dir); + + /** + * Obtain the position that gets transformed to the origin by this matrix. + * This can be used to get the position of the "camera" from a given view transformation matrix. + *

+ * This method is equivalent to the following code: + *

+     * Matrix4x3f inv = new Matrix4x3f(this).invert();
+     * inv.transformPosition(origin.set(0, 0, 0));
+     * 
+ * + * @param origin + * will hold the position transformed to the origin + * @return origin + */ + Vector3f origin(Vector3f origin); + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction light + * and store the result in dest. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param light + * the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f shadow(Vector4fc light, float a, float b, float c, float d, Matrix4x3f dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation + * x*a + y*b + z*c + d = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW) + * and store the result in dest. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + *

+ * Reference: ftp.sgi.com + * + * @param lightX + * the x-component of the light's vector + * @param lightY + * the y-component of the light's vector + * @param lightZ + * the z-component of the light's vector + * @param lightW + * the w-component of the light's vector + * @param a + * the x factor in the plane equation + * @param b + * the y factor in the plane equation + * @param c + * the z factor in the plane equation + * @param d + * the constant in the plane equation + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f shadow(float lightX, float lightY, float lightZ, float lightW, float a, float b, float c, float d, Matrix4x3f dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction light + * and store the result in dest. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If light.w is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param light + * the light's vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f shadow(Vector4fc light, Matrix4x3fc planeTransform, Matrix4x3f dest); + + /** + * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation + * y = 0 as if casting a shadow from a given light position/direction (lightX, lightY, lightZ, lightW) + * and store the result in dest. + *

+ * Before the shadow projection is applied, the plane is transformed via the specified planeTransformation. + *

+ * If lightW is 0.0 the light is being treated as a directional light; if it is 1.0 it is a point light. + *

+ * If M is this matrix and S the shadow matrix, + * then the new matrix will be M * S. So when transforming a + * vector v with the new matrix by using M * S * v, the + * shadow projection will be applied first! + * + * @param lightX + * the x-component of the light vector + * @param lightY + * the y-component of the light vector + * @param lightZ + * the z-component of the light vector + * @param lightW + * the w-component of the light vector + * @param planeTransform + * the transformation to transform the implied plane y = 0 before applying the projection + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f shadow(float lightX, float lightY, float lightZ, float lightW, Matrix4x3fc planeTransform, Matrix4x3f dest); + + /** + * Apply a picking transformation to this matrix using the given window coordinates (x, y) as the pick center + * and the given (width, height) as the size of the picking region in window coordinates, and store the result + * in dest. + * + * @param x + * the x coordinate of the picking region center in window coordinates + * @param y + * the y coordinate of the picking region center in window coordinates + * @param width + * the width of the picking region in window coordinates + * @param height + * the height of the picking region in window coordinates + * @param viewport + * the viewport described by [x, y, width, height] + * @param dest + * the destination matrix, which will hold the result + * @return dest + */ + Matrix4x3f pick(float x, float y, float width, float height, int[] viewport, Matrix4x3f dest); + + /** + * Apply an arcball view transformation to this matrix with the given radius and center (centerX, centerY, centerZ) + * position of the arcball and the specified X and Y rotation angles, and store the result in dest. + *

+ * This method is equivalent to calling: translate(0, 0, -radius, dest).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ) + * + * @param radius + * the arcball radius + * @param centerX + * the x coordinate of the center position of the arcball + * @param centerY + * the y coordinate of the center position of the arcball + * @param centerZ + * the z coordinate of the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f arcball(float radius, float centerX, float centerY, float centerZ, float angleX, float angleY, Matrix4x3f dest); + + /** + * Apply an arcball view transformation to this matrix with the given radius and center + * position of the arcball and the specified X and Y rotation angles, and store the result in dest. + *

+ * This method is equivalent to calling: translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z) + * + * @param radius + * the arcball radius + * @param center + * the center position of the arcball + * @param angleX + * the rotation angle around the X axis in radians + * @param angleY + * the rotation angle around the Y axis in radians + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f arcball(float radius, Vector3fc center, float angleX, float angleY, Matrix4x3f dest); + + /** + * Transform the axis-aligned box given as the minimum corner (minX, minY, minZ) and maximum corner (maxX, maxY, maxZ) + * by this matrix and compute the axis-aligned box of the result whose minimum corner is stored in outMin + * and maximum corner stored in outMax. + *

+ * Reference: http://dev.theomader.com + * + * @param minX + * the x coordinate of the minimum corner of the axis-aligned box + * @param minY + * the y coordinate of the minimum corner of the axis-aligned box + * @param minZ + * the z coordinate of the minimum corner of the axis-aligned box + * @param maxX + * the x coordinate of the maximum corner of the axis-aligned box + * @param maxY + * the y coordinate of the maximum corner of the axis-aligned box + * @param maxZ + * the y coordinate of the maximum corner of the axis-aligned box + * @param outMin + * will hold the minimum corner of the resulting axis-aligned box + * @param outMax + * will hold the maximum corner of the resulting axis-aligned box + * @return this + */ + Matrix4x3f transformAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, Vector3f outMin, Vector3f outMax); + + /** + * Transform the axis-aligned box given as the minimum corner min and maximum corner max + * by this matrix and compute the axis-aligned box of the result whose minimum corner is stored in outMin + * and maximum corner stored in outMax. + * + * @param min + * the minimum corner of the axis-aligned box + * @param max + * the maximum corner of the axis-aligned box + * @param outMin + * will hold the minimum corner of the resulting axis-aligned box + * @param outMax + * will hold the maximum corner of the resulting axis-aligned box + * @return this + */ + Matrix4x3f transformAab(Vector3fc min, Vector3fc max, Vector3f outMin, Vector3f outMax); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other matrix + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f lerp(Matrix4x3fc other, float t, Matrix4x3f dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with dir + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mul(new Matrix4x3f().lookAt(new Vector3f(), new Vector3f(dir).negate(), up).invert(), dest) + * + * @see #rotateTowards(float, float, float, float, float, float, Matrix4x3f) + * + * @param dir + * the direction to rotate towards + * @param up + * the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateTowards(Vector3fc dir, Vector3fc up, Matrix4x3f dest); + + /** + * Apply a model transformation to this matrix for a right-handed coordinate system, + * that aligns the local +Z axis with (dirX, dirY, dirZ) + * and store the result in dest. + *

+ * If M is this matrix and L the lookat matrix, + * then the new matrix will be M * L. So when transforming a + * vector v with the new matrix by using M * L * v, + * the lookat transformation will be applied first! + *

+ * This method is equivalent to calling: mul(new Matrix4x3f().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invert(), dest) + * + * @see #rotateTowards(Vector3fc, Vector3fc, Matrix4x3f) + * + * @param dirX + * the x-coordinate of the direction to rotate towards + * @param dirY + * the y-coordinate of the direction to rotate towards + * @param dirZ + * the z-coordinate of the direction to rotate towards + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f rotateTowards(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Matrix4x3f dest); + + /** + * Extract the Euler angles from the rotation represented by the left 3x3 submatrix of this + * and store the extracted Euler angles in dest. + *

+ * This method assumes that the left 3x3 submatrix of this only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + *

+ * Note that the returned Euler angles must be applied in the order X * Y * Z to obtain the identical matrix. + * This means that calling {@link Matrix4x3fc#rotateXYZ(float, float, float, Matrix4x3f)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix4x3f m = ...; // <- matrix only representing rotation
+     * Matrix4x3f n = new Matrix4x3f();
+     * n.rotateXYZ(m.getEulerAnglesXYZ(new Vector3f()));
+     * 
+ *

+ * Reference: http://nghiaho.com/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3f getEulerAnglesXYZ(Vector3f dest); + + /** + * Extract the Euler angles from the rotation represented by the left 3x3 submatrix of this + * and store the extracted Euler angles in dest. + *

+ * This method assumes that the left 3x3 submatrix of this only represents a rotation without scaling. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + *

+ * Note that the returned Euler angles must be applied in the order Z * Y * X to obtain the identical matrix. + * This means that calling {@link Matrix4x3fc#rotateZYX(float, float, float, Matrix4x3f)} using the obtained Euler angles will yield + * the same rotation as the original matrix from which the Euler angles were obtained, so in the below code the matrix + * m2 should be identical to m (disregarding possible floating-point inaccuracies). + *

+     * Matrix4x3f m = ...; // <- matrix only representing rotation
+     * Matrix4x3f n = new Matrix4x3f();
+     * n.rotateZYX(m.getEulerAnglesZYX(new Vector3f()));
+     * 
+ *

+ * Reference: http://nghiaho.com/ + * + * @param dest + * will hold the extracted Euler angles + * @return dest + */ + Vector3f getEulerAnglesZYX(Vector3f dest); + + /** + * Apply an oblique projection transformation to this matrix with the given values for a and + * b and store the result in dest. + *

+ * If M is this matrix and O the oblique transformation matrix, + * then the new matrix will be M * O. So when transforming a + * vector v with the new matrix by using M * O * v, the + * oblique transformation will be applied first! + *

+ * The oblique transformation is defined as: + *

+     * x' = x + a*z
+     * y' = y + a*z
+     * z' = z
+     * 
+ * or in matrix form: + *
+     * 1 0 a 0
+     * 0 1 b 0
+     * 0 0 1 0
+     * 
+ * + * @param a + * the value for the z factor that applies to x + * @param b + * the value for the z factor that applies to y + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f obliqueZ(float a, float b, Matrix4x3f dest); + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3f)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3f)}) and the + * given vector up, and store the result in dest. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from calling + * {@link Matrix4f#setLookAt(Vector3fc, Vector3fc, Vector3fc)} with the current + * local origin of this matrix (as obtained by {@link #origin(Vector3f)}), the sum of this position and the + * negated local Z axis as well as the given vector up. + * + * @param up + * the up vector + * @param dest + * will hold the result + * @return this + */ + Matrix4x3f withLookAtUp(Vector3fc up, Matrix4x3f dest); + + /** + * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(Vector3f)}) + * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(Vector3f)}) and the + * given vector (upX, upY, upZ), and store the result in dest. + *

+ * This effectively ensures that the resulting matrix will be equal to the one obtained from calling + * {@link Matrix4f#setLookAt(float, float, float, float, float, float, float, float, float)} called with the current + * local origin of this matrix (as obtained by {@link #origin(Vector3f)}), the sum of this position and the + * negated local Z axis as well as the given vector (upX, upY, upZ). + * + * @param upX + * the x coordinate of the up vector + * @param upY + * the y coordinate of the up vector + * @param upZ + * the z coordinate of the up vector + * @param dest + * will hold the result + * @return this + */ + Matrix4x3f withLookAtUp(float upX, float upY, float upZ, Matrix4x3f dest); + /** + * Multiply this by the matrix + *

+     * 1 0 0 0
+     * 0 0 1 0
+     * 0 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapXZY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapXZnY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapXnYnZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapXnZY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapXnZnY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 1 0 0 0
+     * 0 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapYXZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 1 0  0 0
+     * 0 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapYXnZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 1 0 0 0
+     * 0 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapYZX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 0 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapYZnX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 0  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapYnXZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 0  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapYnXnZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapYnZX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 0 -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapYnZnX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 1 0 0
+     * 0 0 1 0
+     * 1 0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapZXY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 1 0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapZXnY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 1 0
+     * 0 1 0 0
+     * 1 0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapZYX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 0 -1 0
+     * 0 1  0 0
+     * 1 0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapZYnX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 1  0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapZnXY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0 -1  0 0
+     * 0  0 -1 0
+     * 1  0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapZnXnY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 1 0
+     * 0 -1 0 0
+     * 1  0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapZnYX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * 0  0 -1 0
+     * 0 -1  0 0
+     * 1  0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapZnYnX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 1  0 0
+     *  0 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnXYnZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 0 1 0
+     *  0 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnXZY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * -1 0  0 0
+     *  0 0 -1 0
+     *  0 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnXZnY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0 -1 0 0
+     *  0  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnXnYZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnXnYnZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * -1  0 0 0
+     *  0  0 1 0
+     *  0 -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnXnZY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     * -1  0  0 0
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnXnZnY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     * -1 0 0 0
+     *  0 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnYXZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     * -1 0  0 0
+     *  0 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnYXnZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     * -1 0 0 0
+     *  0 1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnYZX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     * -1 0  0 0
+     *  0 1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnYZnX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     * -1  0 0 0
+     *  0  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnYnXZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     * -1  0  0 0
+     *  0  0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnYnXnZ(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     * -1  0 0 0
+     *  0 -1 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnYnZX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     * -1  0  0 0
+     *  0 -1  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnYnZnX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 1 0 0
+     *  0 0 1 0
+     * -1 0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnZXY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 1  0 0
+     *  0 0 -1 0
+     * -1 0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnZXnY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 1 0
+     *  0 1 0 0
+     * -1 0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnZYX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 0 -1 0
+     *  0 1  0 0
+     * -1 0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnZYnX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1 0 0
+     *  0  0 1 0
+     * -1  0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnZnXY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0 -1  0 0
+     *  0  0 -1 0
+     * -1  0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnZnXnY(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 1 0
+     *  0 -1 0 0
+     * -1  0 0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnZnYX(Matrix4x3f dest); + /** + * Multiply this by the matrix + *
+     *  0  0 -1 0
+     *  0 -1  0 0
+     * -1  0  0 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f mapnZnYnX(Matrix4x3f dest); + + /** + * Multiply this by the matrix + *
+     * -1 0 0 0
+     *  0 1 0 0
+     *  0 0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f negateX(Matrix4x3f dest); + + /** + * Multiply this by the matrix + *
+     * 1  0 0 0
+     * 0 -1 0 0
+     * 0  0 1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f negateY(Matrix4x3f dest); + + /** + * Multiply this by the matrix + *
+     * 1 0  0 0
+     * 0 1  0 0
+     * 0 0 -1 0
+     * 
+ * and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Matrix4x3f negateZ(Matrix4x3f dest); + + /** + * Compare the matrix elements of this matrix with the given matrix using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param m + * the other matrix + * @param delta + * the allowed maximum difference + * @return true whether all of the matrix elements are equal; false otherwise + */ + boolean equals(Matrix4x3fc m, float delta); + + /** + * Determine whether all matrix elements are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/MemUtil.java b/src/main/java/com/jozufozu/flywheel/repack/joml/MemUtil.java new file mode 100644 index 000000000..c249ff070 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/MemUtil.java @@ -0,0 +1,5572 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.lang.reflect.Field; +import java.nio.*; + +/** + * Helper class to do efficient memory operations on all JOML objects, NIO buffers and primitive arrays. + * This class is used internally throughout JOML, is undocumented and is subject to change. + * Use with extreme caution! + * + * @author The LWJGL authors + * @author Kai Burjack + */ +abstract class MemUtil { + public static final MemUtil INSTANCE = createInstance(); + private static MemUtil createInstance() { + MemUtil accessor; + try { + if (Options.NO_UNSAFE && Options.FORCE_UNSAFE) + throw new ConfigurationException("Cannot enable both -Djoml.nounsafe and -Djoml.forceUnsafe", null); + else if (Options.NO_UNSAFE) + accessor = new MemUtilNIO(); + else + accessor = new MemUtilUnsafe(); + } catch (Throwable e) { + if (Options.FORCE_UNSAFE) + throw new ConfigurationException("Unsafe is not supported but its use was forced via -Djoml.forceUnsafe", e); + accessor = new MemUtilNIO(); + } + return accessor; + } + + public abstract void put(Matrix4f m, int offset, FloatBuffer dest); + public abstract void put(Matrix4f m, int offset, ByteBuffer dest); + public abstract void put(Matrix4x3f m, int offset, FloatBuffer dest); + public abstract void put(Matrix4x3f m, int offset, ByteBuffer dest); + public abstract void put4x4(Matrix4x3f m, int offset, FloatBuffer dest); + public abstract void put4x4(Matrix4x3f m, int offset, ByteBuffer dest); + public abstract void put4x4(Matrix4x3d m, int offset, DoubleBuffer dest); + public abstract void put4x4(Matrix4x3d m, int offset, ByteBuffer dest); + public abstract void put4x4(Matrix3x2f m, int offset, FloatBuffer dest); + public abstract void put4x4(Matrix3x2f m, int offset, ByteBuffer dest); + public abstract void put4x4(Matrix3x2d m, int offset, DoubleBuffer dest); + public abstract void put4x4(Matrix3x2d m, int offset, ByteBuffer dest); + public abstract void put3x3(Matrix3x2f m, int offset, FloatBuffer dest); + public abstract void put3x3(Matrix3x2f m, int offset, ByteBuffer dest); + public abstract void put3x3(Matrix3x2d m, int offset, DoubleBuffer dest); + public abstract void put3x3(Matrix3x2d m, int offset, ByteBuffer dest); + public abstract void put4x3(Matrix4f m, int offset, FloatBuffer dest); + public abstract void put4x3(Matrix4f m, int offset, ByteBuffer dest); + public abstract void put3x4(Matrix4f m, int offset, FloatBuffer dest); + public abstract void put3x4(Matrix4f m, int offset, ByteBuffer dest); + public abstract void put3x4(Matrix4x3f m, int offset, FloatBuffer dest); + public abstract void put3x4(Matrix4x3f m, int offset, ByteBuffer dest); + public abstract void put3x4(Matrix3f m, int offset, FloatBuffer dest); + public abstract void put3x4(Matrix3f m, int offset, ByteBuffer dest); + public abstract void putTransposed(Matrix4f m, int offset, FloatBuffer dest); + public abstract void putTransposed(Matrix4f m, int offset, ByteBuffer dest); + public abstract void put4x3Transposed(Matrix4f m, int offset, FloatBuffer dest); + public abstract void put4x3Transposed(Matrix4f m, int offset, ByteBuffer dest); + public abstract void putTransposed(Matrix4x3f m, int offset, FloatBuffer dest); + public abstract void putTransposed(Matrix4x3f m, int offset, ByteBuffer dest); + public abstract void putTransposed(Matrix3f m, int offset, FloatBuffer dest); + public abstract void putTransposed(Matrix3f m, int offset, ByteBuffer dest); + public abstract void putTransposed(Matrix2f m, int offset, FloatBuffer dest); + public abstract void putTransposed(Matrix2f m, int offset, ByteBuffer dest); + public abstract void put(Matrix4d m, int offset, DoubleBuffer dest); + public abstract void put(Matrix4d m, int offset, ByteBuffer dest); + public abstract void put(Matrix4x3d m, int offset, DoubleBuffer dest); + public abstract void put(Matrix4x3d m, int offset, ByteBuffer dest); + public abstract void putf(Matrix4d m, int offset, FloatBuffer dest); + public abstract void putf(Matrix4d m, int offset, ByteBuffer dest); + public abstract void putf(Matrix4x3d m, int offset, FloatBuffer dest); + public abstract void putf(Matrix4x3d m, int offset, ByteBuffer dest); + public abstract void putTransposed(Matrix4d m, int offset, DoubleBuffer dest); + public abstract void putTransposed(Matrix4d m, int offset, ByteBuffer dest); + public abstract void put4x3Transposed(Matrix4d m, int offset, DoubleBuffer dest); + public abstract void put4x3Transposed(Matrix4d m, int offset, ByteBuffer dest); + public abstract void putTransposed(Matrix4x3d m, int offset, DoubleBuffer dest); + public abstract void putTransposed(Matrix4x3d m, int offset, ByteBuffer dest); + public abstract void putTransposed(Matrix2d m, int offset, DoubleBuffer dest); + public abstract void putTransposed(Matrix2d m, int offset, ByteBuffer dest); + public abstract void putfTransposed(Matrix4d m, int offset, FloatBuffer dest); + public abstract void putfTransposed(Matrix4d m, int offset, ByteBuffer dest); + public abstract void putfTransposed(Matrix4x3d m, int offset, FloatBuffer dest); + public abstract void putfTransposed(Matrix4x3d m, int offset, ByteBuffer dest); + public abstract void putfTransposed(Matrix2d m, int offset, FloatBuffer dest); + public abstract void putfTransposed(Matrix2d m, int offset, ByteBuffer dest); + public abstract void put(Matrix3f m, int offset, FloatBuffer dest); + public abstract void put(Matrix3f m, int offset, ByteBuffer dest); + public abstract void put(Matrix3d m, int offset, DoubleBuffer dest); + public abstract void put(Matrix3d m, int offset, ByteBuffer dest); + public abstract void putf(Matrix3d m, int offset, FloatBuffer dest); + public abstract void putf(Matrix3d m, int offset, ByteBuffer dest); + public abstract void put(Matrix3x2f m, int offset, FloatBuffer dest); + public abstract void put(Matrix3x2f m, int offset, ByteBuffer dest); + public abstract void put(Matrix3x2d m, int offset, DoubleBuffer dest); + public abstract void put(Matrix3x2d m, int offset, ByteBuffer dest); + public abstract void put(Matrix2f m, int offset, FloatBuffer dest); + public abstract void put(Matrix2f m, int offset, ByteBuffer dest); + public abstract void put(Matrix2d m, int offset, DoubleBuffer dest); + public abstract void put(Matrix2d m, int offset, ByteBuffer dest); + public abstract void putf(Matrix2d m, int offset, FloatBuffer dest); + public abstract void putf(Matrix2d m, int offset, ByteBuffer dest); + public abstract void put(Vector4d src, int offset, DoubleBuffer dest); + public abstract void put(Vector4d src, int offset, FloatBuffer dest); + public abstract void put(Vector4d src, int offset, ByteBuffer dest); + public abstract void putf(Vector4d src, int offset, ByteBuffer dest); + public abstract void put(Vector4f src, int offset, FloatBuffer dest); + public abstract void put(Vector4f src, int offset, ByteBuffer dest); + public abstract void put(Vector4i src, int offset, IntBuffer dest); + public abstract void put(Vector4i src, int offset, ByteBuffer dest); + public abstract void put(Vector3f src, int offset, FloatBuffer dest); + public abstract void put(Vector3f src, int offset, ByteBuffer dest); + public abstract void put(Vector3d src, int offset, DoubleBuffer dest); + public abstract void put(Vector3d src, int offset, FloatBuffer dest); + public abstract void put(Vector3d src, int offset, ByteBuffer dest); + public abstract void putf(Vector3d src, int offset, ByteBuffer dest); + public abstract void put(Vector3i src, int offset, IntBuffer dest); + public abstract void put(Vector3i src, int offset, ByteBuffer dest); + public abstract void put(Vector2f src, int offset, FloatBuffer dest); + public abstract void put(Vector2f src, int offset, ByteBuffer dest); + public abstract void put(Vector2d src, int offset, DoubleBuffer dest); + public abstract void put(Vector2d src, int offset, ByteBuffer dest); + public abstract void put(Vector2i src, int offset, IntBuffer dest); + public abstract void put(Vector2i src, int offset, ByteBuffer dest); + public abstract void get(Matrix4f m, int offset, FloatBuffer src); + public abstract void get(Matrix4f m, int offset, ByteBuffer src); + public abstract void getTransposed(Matrix4f m, int offset, FloatBuffer src); + public abstract void getTransposed(Matrix4f m, int offset, ByteBuffer src); + public abstract void get(Matrix4x3f m, int offset, FloatBuffer src); + public abstract void get(Matrix4x3f m, int offset, ByteBuffer src); + public abstract void get(Matrix4d m, int offset, DoubleBuffer src); + public abstract void get(Matrix4d m, int offset, ByteBuffer src); + public abstract void get(Matrix4x3d m, int offset, DoubleBuffer src); + public abstract void get(Matrix4x3d m, int offset, ByteBuffer src); + public abstract void getf(Matrix4d m, int offset, FloatBuffer src); + public abstract void getf(Matrix4d m, int offset, ByteBuffer src); + public abstract void getf(Matrix4x3d m, int offset, FloatBuffer src); + public abstract void getf(Matrix4x3d m, int offset, ByteBuffer src); + public abstract void get(Matrix3f m, int offset, FloatBuffer src); + public abstract void get(Matrix3f m, int offset, ByteBuffer src); + public abstract void get(Matrix3d m, int offset, DoubleBuffer src); + public abstract void get(Matrix3d m, int offset, ByteBuffer src); + public abstract void get(Matrix3x2f m, int offset, FloatBuffer src); + public abstract void get(Matrix3x2f m, int offset, ByteBuffer src); + public abstract void get(Matrix3x2d m, int offset, DoubleBuffer src); + public abstract void get(Matrix3x2d m, int offset, ByteBuffer src); + public abstract void getf(Matrix3d m, int offset, FloatBuffer src); + public abstract void getf(Matrix3d m, int offset, ByteBuffer src); + public abstract void get(Matrix2f m, int offset, FloatBuffer src); + public abstract void get(Matrix2f m, int offset, ByteBuffer src); + public abstract void get(Matrix2d m, int offset, DoubleBuffer src); + public abstract void get(Matrix2d m, int offset, ByteBuffer src); + public abstract void getf(Matrix2d m, int offset, FloatBuffer src); + public abstract void getf(Matrix2d m, int offset, ByteBuffer src); + public abstract void get(Vector4d dst, int offset, DoubleBuffer src); + public abstract void get(Vector4d dst, int offset, ByteBuffer src); + public abstract void get(Vector4f dst, int offset, FloatBuffer src); + public abstract void get(Vector4f dst, int offset, ByteBuffer src); + public abstract void get(Vector4i dst, int offset, IntBuffer src); + public abstract void get(Vector4i dst, int offset, ByteBuffer src); + public abstract void get(Vector3f dst, int offset, FloatBuffer src); + public abstract void get(Vector3f dst, int offset, ByteBuffer src); + public abstract void get(Vector3d dst, int offset, DoubleBuffer src); + public abstract void get(Vector3d dst, int offset, ByteBuffer src); + public abstract void get(Vector3i dst, int offset, IntBuffer src); + public abstract void get(Vector3i dst, int offset, ByteBuffer src); + public abstract void get(Vector2f dst, int offset, FloatBuffer src); + public abstract void get(Vector2f dst, int offset, ByteBuffer src); + public abstract void get(Vector2d dst, int offset, DoubleBuffer src); + public abstract void get(Vector2d dst, int offset, ByteBuffer src); + public abstract void get(Vector2i dst, int offset, IntBuffer src); + public abstract void get(Vector2i dst, int offset, ByteBuffer src); + public abstract void putMatrix3f(Quaternionf q, int position, ByteBuffer dest); + public abstract void putMatrix3f(Quaternionf q, int position, FloatBuffer dest); + public abstract void putMatrix4f(Quaternionf q, int position, ByteBuffer dest); + public abstract void putMatrix4f(Quaternionf q, int position, FloatBuffer dest); + public abstract void putMatrix4x3f(Quaternionf q, int position, ByteBuffer dest); + public abstract void putMatrix4x3f(Quaternionf q, int position, FloatBuffer dest); + + public abstract float get(Matrix4f m, int column, int row); + public abstract Matrix4f set(Matrix4f m, int column, int row, float v); + public abstract double get(Matrix4d m, int column, int row); + public abstract Matrix4d set(Matrix4d m, int column, int row, double v); + public abstract float get(Matrix3f m, int column, int row); + public abstract Matrix3f set(Matrix3f m, int column, int row, float v); + public abstract double get(Matrix3d m, int column, int row); + public abstract Matrix3d set(Matrix3d m, int column, int row, double v); + public abstract Vector4f getColumn(Matrix4f m, int column, Vector4f dest); + public abstract Matrix4f setColumn(Vector4f v, int column, Matrix4f dest); + public abstract Matrix4f setColumn(Vector4fc v, int column, Matrix4f dest); + public abstract void copy(Matrix4f src, Matrix4f dest); + public abstract void copy(Matrix4x3f src, Matrix4x3f dest); + public abstract void copy(Matrix4f src, Matrix4x3f dest); + public abstract void copy(Matrix4x3f src, Matrix4f dest); + public abstract void copy(Matrix3f src, Matrix3f dest); + public abstract void copy(Matrix3f src, Matrix4f dest); + public abstract void copy(Matrix4f src, Matrix3f dest); + public abstract void copy(Matrix3f src, Matrix4x3f dest); + public abstract void copy(Matrix3x2f src, Matrix3x2f dest); + public abstract void copy(Matrix3x2d src, Matrix3x2d dest); + public abstract void copy(Matrix2f src, Matrix2f dest); + public abstract void copy(Matrix2d src, Matrix2d dest); + public abstract void copy(Matrix2f src, Matrix3f dest); + public abstract void copy(Matrix3f src, Matrix2f dest); + public abstract void copy(Matrix2f src, Matrix3x2f dest); + public abstract void copy(Matrix3x2f src, Matrix2f dest); + public abstract void copy(Matrix2d src, Matrix3d dest); + public abstract void copy(Matrix3d src, Matrix2d dest); + public abstract void copy(Matrix2d src, Matrix3x2d dest); + public abstract void copy(Matrix3x2d src, Matrix2d dest); + public abstract void copy3x3(Matrix4f src, Matrix4f dest); + public abstract void copy3x3(Matrix4x3f src, Matrix4x3f dest); + public abstract void copy3x3(Matrix3f src, Matrix4x3f dest); + public abstract void copy3x3(Matrix3f src, Matrix4f dest); + public abstract void copy4x3(Matrix4f src, Matrix4f dest); + public abstract void copy4x3(Matrix4x3f src, Matrix4f dest); + public abstract void copy(float[] arr, int off, Matrix4f dest); + public abstract void copyTransposed(float[] arr, int off, Matrix4f dest); + public abstract void copy(float[] arr, int off, Matrix3f dest); + public abstract void copy(float[] arr, int off, Matrix4x3f dest); + public abstract void copy(float[] arr, int off, Matrix3x2f dest); + public abstract void copy(double[] arr, int off, Matrix3x2d dest); + public abstract void copy(float[] arr, int off, Matrix2f dest); + public abstract void copy(double[] arr, int off, Matrix2d dest); + public abstract void copy(Matrix4f src, float[] dest, int off); + public abstract void copy(Matrix3f src, float[] dest, int off); + public abstract void copy(Matrix4x3f src, float[] dest, int off); + public abstract void copy(Matrix3x2f src, float[] dest, int off); + public abstract void copy(Matrix3x2d src, double[] dest, int off); + public abstract void copy(Matrix2f src, float[] dest, int off); + public abstract void copy(Matrix2d src, double[] dest, int off); + public abstract void copy4x4(Matrix4x3f src, float[] dest, int off); + public abstract void copy4x4(Matrix4x3d src, float[] dest, int off); + public abstract void copy4x4(Matrix4x3d src, double[] dest, int off); + public abstract void copy4x4(Matrix3x2f src, float[] dest, int off); + public abstract void copy4x4(Matrix3x2d src, double[] dest, int off); + public abstract void copy3x3(Matrix3x2f src, float[] dest, int off); + public abstract void copy3x3(Matrix3x2d src, double[] dest, int off); + public abstract void identity(Matrix4f dest); + public abstract void identity(Matrix4x3f dest); + public abstract void identity(Matrix3f dest); + public abstract void identity(Matrix3x2f dest); + public abstract void identity(Matrix3x2d dest); + public abstract void identity(Matrix2f dest); + public abstract void swap(Matrix4f m1, Matrix4f m2); + public abstract void swap(Matrix4x3f m1, Matrix4x3f m2); + public abstract void swap(Matrix3f m1, Matrix3f m2); + public abstract void swap(Matrix2f m1, Matrix2f m2); + public abstract void swap(Matrix2d m1, Matrix2d m2); + public abstract void zero(Matrix4f dest); + public abstract void zero(Matrix4x3f dest); + public abstract void zero(Matrix3f dest); + public abstract void zero(Matrix3x2f dest); + public abstract void zero(Matrix3x2d dest); + public abstract void zero(Matrix2f dest); + public abstract void zero(Matrix2d dest); + + public static class MemUtilNIO extends MemUtil { + public void put0(Matrix4f m, FloatBuffer dest) { + dest.put(0, m.m00()) + .put(1, m.m01()) + .put(2, m.m02()) + .put(3, m.m03()) + .put(4, m.m10()) + .put(5, m.m11()) + .put(6, m.m12()) + .put(7, m.m13()) + .put(8, m.m20()) + .put(9, m.m21()) + .put(10, m.m22()) + .put(11, m.m23()) + .put(12, m.m30()) + .put(13, m.m31()) + .put(14, m.m32()) + .put(15, m.m33()); + } + public void putN(Matrix4f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, m.m03()) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, m.m12()) + .put(offset+7, m.m13()) + .put(offset+8, m.m20()) + .put(offset+9, m.m21()) + .put(offset+10, m.m22()) + .put(offset+11, m.m23()) + .put(offset+12, m.m30()) + .put(offset+13, m.m31()) + .put(offset+14, m.m32()) + .put(offset+15, m.m33()); + } + public void put(Matrix4f m, int offset, FloatBuffer dest) { + if (offset == 0) + put0(m, dest); + else + putN(m, offset, dest); + } + + public void put0(Matrix4f m, ByteBuffer dest) { + dest.putFloat(0, m.m00()) + .putFloat(4, m.m01()) + .putFloat(8, m.m02()) + .putFloat(12, m.m03()) + .putFloat(16, m.m10()) + .putFloat(20, m.m11()) + .putFloat(24, m.m12()) + .putFloat(28, m.m13()) + .putFloat(32, m.m20()) + .putFloat(36, m.m21()) + .putFloat(40, m.m22()) + .putFloat(44, m.m23()) + .putFloat(48, m.m30()) + .putFloat(52, m.m31()) + .putFloat(56, m.m32()) + .putFloat(60, m.m33()); + } + private void putN(Matrix4f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m02()) + .putFloat(offset+12, m.m03()) + .putFloat(offset+16, m.m10()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, m.m12()) + .putFloat(offset+28, m.m13()) + .putFloat(offset+32, m.m20()) + .putFloat(offset+36, m.m21()) + .putFloat(offset+40, m.m22()) + .putFloat(offset+44, m.m23()) + .putFloat(offset+48, m.m30()) + .putFloat(offset+52, m.m31()) + .putFloat(offset+56, m.m32()) + .putFloat(offset+60, m.m33()); + } + public void put(Matrix4f m, int offset, ByteBuffer dest) { + if (offset == 0) + put0(m, dest); + else + putN(m, offset, dest); + } + + public void put4x3_0(Matrix4f m, FloatBuffer dest) { + dest.put(0, m.m00()) + .put(1, m.m01()) + .put(2, m.m02()) + .put(3, m.m10()) + .put(4, m.m11()) + .put(5, m.m12()) + .put(6, m.m20()) + .put(7, m.m21()) + .put(8, m.m22()) + .put(9, m.m30()) + .put(10, m.m31()) + .put(11, m.m32()); + } + public void put4x3_N(Matrix4f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, m.m10()) + .put(offset+4, m.m11()) + .put(offset+5, m.m12()) + .put(offset+6, m.m20()) + .put(offset+7, m.m21()) + .put(offset+8, m.m22()) + .put(offset+9, m.m30()) + .put(offset+10, m.m31()) + .put(offset+11, m.m32()); + } + public void put4x3(Matrix4f m, int offset, FloatBuffer dest) { + if (offset == 0) + put4x3_0(m, dest); + else + put4x3_N(m, offset, dest); + } + + public void put4x3_0(Matrix4f m, ByteBuffer dest) { + dest.putFloat(0, m.m00()) + .putFloat(4, m.m01()) + .putFloat(8, m.m02()) + .putFloat(12, m.m10()) + .putFloat(16, m.m11()) + .putFloat(20, m.m12()) + .putFloat(24, m.m20()) + .putFloat(28, m.m21()) + .putFloat(32, m.m22()) + .putFloat(36, m.m30()) + .putFloat(40, m.m31()) + .putFloat(44, m.m32()); + } + private void put4x3_N(Matrix4f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m02()) + .putFloat(offset+12, m.m10()) + .putFloat(offset+16, m.m11()) + .putFloat(offset+20, m.m12()) + .putFloat(offset+24, m.m20()) + .putFloat(offset+28, m.m21()) + .putFloat(offset+32, m.m22()) + .putFloat(offset+36, m.m30()) + .putFloat(offset+40, m.m31()) + .putFloat(offset+44, m.m32()); + } + public void put4x3(Matrix4f m, int offset, ByteBuffer dest) { + if (offset == 0) + put4x3_0(m, dest); + else + put4x3_N(m, offset, dest); + } + + public void put3x4_0(Matrix4f m, ByteBuffer dest) { + dest.putFloat(0, m.m00()) + .putFloat(4, m.m01()) + .putFloat(8, m.m02()) + .putFloat(12, m.m03()) + .putFloat(16, m.m10()) + .putFloat(20, m.m11()) + .putFloat(24, m.m12()) + .putFloat(28, m.m13()) + .putFloat(32, m.m20()) + .putFloat(36, m.m21()) + .putFloat(40, m.m22()) + .putFloat(44, m.m23()); + } + private void put3x4_N(Matrix4f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m02()) + .putFloat(offset+12, m.m03()) + .putFloat(offset+16, m.m10()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, m.m12()) + .putFloat(offset+28, m.m13()) + .putFloat(offset+32, m.m20()) + .putFloat(offset+36, m.m21()) + .putFloat(offset+40, m.m22()) + .putFloat(offset+44, m.m23()); + } + public void put3x4(Matrix4f m, int offset, ByteBuffer dest) { + if (offset == 0) + put3x4_0(m, dest); + else + put3x4_N(m, offset, dest); + } + + public void put3x4_0(Matrix4f m, FloatBuffer dest) { + dest.put(0, m.m00()) + .put(1, m.m01()) + .put(2, m.m02()) + .put(3, m.m03()) + .put(4, m.m10()) + .put(5, m.m11()) + .put(6, m.m12()) + .put(7, m.m13()) + .put(8, m.m20()) + .put(9, m.m21()) + .put(10, m.m22()) + .put(11, m.m23()); + } + public void put3x4_N(Matrix4f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, m.m03()) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, m.m12()) + .put(offset+7, m.m13()) + .put(offset+8, m.m20()) + .put(offset+9, m.m21()) + .put(offset+10, m.m22()) + .put(offset+11, m.m23()); + } + public void put3x4(Matrix4f m, int offset, FloatBuffer dest) { + if (offset == 0) + put3x4_0(m, dest); + else + put3x4_N(m, offset, dest); + } + + public void put3x4_0(Matrix4x3f m, ByteBuffer dest) { + dest.putFloat(0, m.m00()) + .putFloat(4, m.m01()) + .putFloat(8, m.m02()) + .putFloat(12, 0.0f) + .putFloat(16, m.m10()) + .putFloat(20, m.m11()) + .putFloat(24, m.m12()) + .putFloat(28, 0.0f) + .putFloat(32, m.m20()) + .putFloat(36, m.m21()) + .putFloat(40, m.m22()) + .putFloat(44, 0.0f); + } + private void put3x4_N(Matrix4x3f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m02()) + .putFloat(offset+12, 0.0f) + .putFloat(offset+16, m.m10()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, m.m12()) + .putFloat(offset+28, 0.0f) + .putFloat(offset+32, m.m20()) + .putFloat(offset+36, m.m21()) + .putFloat(offset+40, m.m22()) + .putFloat(offset+44, 0.0f); + } + public void put3x4(Matrix4x3f m, int offset, ByteBuffer dest) { + if (offset == 0) + put3x4_0(m, dest); + else + put3x4_N(m, offset, dest); + } + + public void put3x4_0(Matrix4x3f m, FloatBuffer dest) { + dest.put(0, m.m00()) + .put(1, m.m01()) + .put(2, m.m02()) + .put(3, 0.0f) + .put(4, m.m10()) + .put(5, m.m11()) + .put(6, m.m12()) + .put(7, 0.0f) + .put(8, m.m20()) + .put(9, m.m21()) + .put(10, m.m22()) + .put(11, 0.0f); + } + public void put3x4_N(Matrix4x3f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, 0.0f) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, m.m12()) + .put(offset+7, 0.0f) + .put(offset+8, m.m20()) + .put(offset+9, m.m21()) + .put(offset+10, m.m22()) + .put(offset+11, 0.0f); + } + public void put3x4(Matrix4x3f m, int offset, FloatBuffer dest) { + if (offset == 0) + put3x4_0(m, dest); + else + put3x4_N(m, offset, dest); + } + + public void put0(Matrix4x3f m, FloatBuffer dest) { + dest.put(0, m.m00()) + .put(1, m.m01()) + .put(2, m.m02()) + .put(3, m.m10()) + .put(4, m.m11()) + .put(5, m.m12()) + .put(6, m.m20()) + .put(7, m.m21()) + .put(8, m.m22()) + .put(9, m.m30()) + .put(10, m.m31()) + .put(11, m.m32()); + } + public void putN(Matrix4x3f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, m.m10()) + .put(offset+4, m.m11()) + .put(offset+5, m.m12()) + .put(offset+6, m.m20()) + .put(offset+7, m.m21()) + .put(offset+8, m.m22()) + .put(offset+9, m.m30()) + .put(offset+10, m.m31()) + .put(offset+11, m.m32()); + } + public void put(Matrix4x3f m, int offset, FloatBuffer dest) { + if (offset == 0) + put0(m, dest); + else + putN(m, offset, dest); + } + + public void put0(Matrix4x3f m, ByteBuffer dest) { + dest.putFloat(0, m.m00()) + .putFloat(4, m.m01()) + .putFloat(8, m.m02()) + .putFloat(12, m.m10()) + .putFloat(16, m.m11()) + .putFloat(20, m.m12()) + .putFloat(24, m.m20()) + .putFloat(28, m.m21()) + .putFloat(32, m.m22()) + .putFloat(36, m.m30()) + .putFloat(40, m.m31()) + .putFloat(44, m.m32()); + } + public void putN(Matrix4x3f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m02()) + .putFloat(offset+12, m.m10()) + .putFloat(offset+16, m.m11()) + .putFloat(offset+20, m.m12()) + .putFloat(offset+24, m.m20()) + .putFloat(offset+28, m.m21()) + .putFloat(offset+32, m.m22()) + .putFloat(offset+36, m.m30()) + .putFloat(offset+40, m.m31()) + .putFloat(offset+44, m.m32()); + } + public void put(Matrix4x3f m, int offset, ByteBuffer dest) { + if (offset == 0) + put0(m, dest); + else + putN(m, offset, dest); + } + + public void put4x4(Matrix4x3f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, 0.0f) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, m.m12()) + .put(offset+7, 0.0f) + .put(offset+8, m.m20()) + .put(offset+9, m.m21()) + .put(offset+10, m.m22()) + .put(offset+11, 0.0f) + .put(offset+12, m.m30()) + .put(offset+13, m.m31()) + .put(offset+14, m.m32()) + .put(offset+15, 1.0f); + } + + public void put4x4(Matrix4x3f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m02()) + .putFloat(offset+12, 0.0f) + .putFloat(offset+16, m.m10()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, m.m12()) + .putFloat(offset+28, 0.0f) + .putFloat(offset+32, m.m20()) + .putFloat(offset+36, m.m21()) + .putFloat(offset+40, m.m22()) + .putFloat(offset+44, 0.0f) + .putFloat(offset+48, m.m30()) + .putFloat(offset+52, m.m31()) + .putFloat(offset+56, m.m32()) + .putFloat(offset+60, 1.0f); + } + + public void put4x4(Matrix4x3d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, 0.0) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, m.m12()) + .put(offset+7, 0.0) + .put(offset+8, m.m20()) + .put(offset+9, m.m21()) + .put(offset+10, m.m22()) + .put(offset+11, 0.0) + .put(offset+12, m.m30()) + .put(offset+13, m.m31()) + .put(offset+14, m.m32()) + .put(offset+15, 1.0); + } + + public void put4x4(Matrix4x3d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m01()) + .putDouble(offset+16, m.m02()) + .putDouble(offset+24, 0.0) + .putDouble(offset+32, m.m10()) + .putDouble(offset+40, m.m11()) + .putDouble(offset+48, m.m12()) + .putDouble(offset+56, 0.0) + .putDouble(offset+64, m.m20()) + .putDouble(offset+72, m.m21()) + .putDouble(offset+80, m.m22()) + .putDouble(offset+88, 0.0) + .putDouble(offset+96, m.m30()) + .putDouble(offset+104, m.m31()) + .putDouble(offset+112, m.m32()) + .putDouble(offset+120, 1.0); + } + + public void put4x4(Matrix3x2f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, 0.0f) + .put(offset+3, 0.0f) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, 0.0f) + .put(offset+7, 0.0f) + .put(offset+8, 0.0f) + .put(offset+9, 0.0f) + .put(offset+10, 1.0f) + .put(offset+11, 0.0f) + .put(offset+12, m.m20()) + .put(offset+13, m.m21()) + .put(offset+14, 0.0f) + .put(offset+15, 1.0f); + } + + public void put4x4(Matrix3x2f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, 0.0f) + .putFloat(offset+12, 0.0f) + .putFloat(offset+16, m.m10()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, 0.0f) + .putFloat(offset+28, 0.0f) + .putFloat(offset+32, 0.0f) + .putFloat(offset+36, 0.0f) + .putFloat(offset+40, 1.0f) + .putFloat(offset+44, 0.0f) + .putFloat(offset+48, m.m20()) + .putFloat(offset+52, m.m21()) + .putFloat(offset+56, 0.0f) + .putFloat(offset+60, 1.0f); + } + + public void put4x4(Matrix3x2d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, 0.0) + .put(offset+3, 0.0) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, 0.0) + .put(offset+7, 0.0) + .put(offset+8, 0.0) + .put(offset+9, 0.0) + .put(offset+10, 1.0) + .put(offset+11, 0.0) + .put(offset+12, m.m20()) + .put(offset+13, m.m21()) + .put(offset+14, 0.0) + .put(offset+15, 1.0); + } + + public void put4x4(Matrix3x2d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m01()) + .putDouble(offset+16, 0.0) + .putDouble(offset+24, 0.0) + .putDouble(offset+32, m.m10()) + .putDouble(offset+40, m.m11()) + .putDouble(offset+48, 0.0) + .putDouble(offset+56, 0.0) + .putDouble(offset+64, 0.0) + .putDouble(offset+72, 0.0) + .putDouble(offset+80, 1.0) + .putDouble(offset+88, 0.0) + .putDouble(offset+96, m.m20()) + .putDouble(offset+104, m.m21()) + .putDouble(offset+112, 0.0) + .putDouble(offset+120, 1.0); + } + + public void put3x3(Matrix3x2f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, 0.0f) + .put(offset+3, m.m10()) + .put(offset+4, m.m11()) + .put(offset+5, 0.0f) + .put(offset+6, m.m20()) + .put(offset+7, m.m21()) + .put(offset+8, 1.0f); + } + + public void put3x3(Matrix3x2f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, 0.0f) + .putFloat(offset+12, m.m10()) + .putFloat(offset+16, m.m11()) + .putFloat(offset+20, 0.0f) + .putFloat(offset+24, m.m20()) + .putFloat(offset+28, m.m21()) + .putFloat(offset+32, 1.0f); + } + + public void put3x3(Matrix3x2d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, 0.0) + .put(offset+3, m.m10()) + .put(offset+4, m.m11()) + .put(offset+5, 0.0) + .put(offset+6, m.m20()) + .put(offset+7, m.m21()) + .put(offset+8, 1.0); + } + + public void put3x3(Matrix3x2d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m01()) + .putDouble(offset+16, 0.0) + .putDouble(offset+24, m.m10()) + .putDouble(offset+32, m.m11()) + .putDouble(offset+40, 0.0) + .putDouble(offset+48, m.m20()) + .putDouble(offset+56, m.m21()) + .putDouble(offset+64, 1.0); + } + + private void putTransposedN(Matrix4f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m20()) + .put(offset+3, m.m30()) + .put(offset+4, m.m01()) + .put(offset+5, m.m11()) + .put(offset+6, m.m21()) + .put(offset+7, m.m31()) + .put(offset+8, m.m02()) + .put(offset+9, m.m12()) + .put(offset+10, m.m22()) + .put(offset+11, m.m32()) + .put(offset+12, m.m03()) + .put(offset+13, m.m13()) + .put(offset+14, m.m23()) + .put(offset+15, m.m33()); + } + private void putTransposed0(Matrix4f m, FloatBuffer dest) { + dest.put(0, m.m00()) + .put(1, m.m10()) + .put(2, m.m20()) + .put(3, m.m30()) + .put(4, m.m01()) + .put(5, m.m11()) + .put(6, m.m21()) + .put(7, m.m31()) + .put(8, m.m02()) + .put(9, m.m12()) + .put(10, m.m22()) + .put(11, m.m32()) + .put(12, m.m03()) + .put(13, m.m13()) + .put(14, m.m23()) + .put(15, m.m33()); + } + public void putTransposed(Matrix4f m, int offset, FloatBuffer dest) { + if (offset == 0) + putTransposed0(m, dest); + else + putTransposedN(m, offset, dest); + } + + private void putTransposedN(Matrix4f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m10()) + .putFloat(offset+8, m.m20()) + .putFloat(offset+12, m.m30()) + .putFloat(offset+16, m.m01()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, m.m21()) + .putFloat(offset+28, m.m31()) + .putFloat(offset+32, m.m02()) + .putFloat(offset+36, m.m12()) + .putFloat(offset+40, m.m22()) + .putFloat(offset+44, m.m32()) + .putFloat(offset+48, m.m03()) + .putFloat(offset+52, m.m13()) + .putFloat(offset+56, m.m23()) + .putFloat(offset+60, m.m33()); + } + private void putTransposed0(Matrix4f m, ByteBuffer dest) { + dest.putFloat(0, m.m00()) + .putFloat(4, m.m10()) + .putFloat(8, m.m20()) + .putFloat(12, m.m30()) + .putFloat(16, m.m01()) + .putFloat(20, m.m11()) + .putFloat(24, m.m21()) + .putFloat(28, m.m31()) + .putFloat(32, m.m02()) + .putFloat(36, m.m12()) + .putFloat(40, m.m22()) + .putFloat(44, m.m32()) + .putFloat(48, m.m03()) + .putFloat(52, m.m13()) + .putFloat(56, m.m23()) + .putFloat(60, m.m33()); + } + public void putTransposed(Matrix4f m, int offset, ByteBuffer dest) { + if (offset == 0) + putTransposed0(m, dest); + else + putTransposedN(m, offset, dest); + } + + public void put4x3Transposed(Matrix4f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m20()) + .put(offset+3, m.m30()) + .put(offset+4, m.m01()) + .put(offset+5, m.m11()) + .put(offset+6, m.m21()) + .put(offset+7, m.m31()) + .put(offset+8, m.m02()) + .put(offset+9, m.m12()) + .put(offset+10, m.m22()) + .put(offset+11, m.m32()); + } + + public void put4x3Transposed(Matrix4f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m10()) + .putFloat(offset+8, m.m20()) + .putFloat(offset+12, m.m30()) + .putFloat(offset+16, m.m01()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, m.m21()) + .putFloat(offset+28, m.m31()) + .putFloat(offset+32, m.m02()) + .putFloat(offset+36, m.m12()) + .putFloat(offset+40, m.m22()) + .putFloat(offset+44, m.m32()); + } + + public void putTransposed(Matrix4x3f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m20()) + .put(offset+3, m.m30()) + .put(offset+4, m.m01()) + .put(offset+5, m.m11()) + .put(offset+6, m.m21()) + .put(offset+7, m.m31()) + .put(offset+8, m.m02()) + .put(offset+9, m.m12()) + .put(offset+10, m.m22()) + .put(offset+11, m.m32()); + } + + public void putTransposed(Matrix4x3f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m10()) + .putFloat(offset+8, m.m20()) + .putFloat(offset+12, m.m30()) + .putFloat(offset+16, m.m01()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, m.m21()) + .putFloat(offset+28, m.m31()) + .putFloat(offset+32, m.m02()) + .putFloat(offset+36, m.m12()) + .putFloat(offset+40, m.m22()) + .putFloat(offset+44, m.m32()); + } + + public void putTransposed(Matrix3f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m20()) + .put(offset+3, m.m01()) + .put(offset+4, m.m11()) + .put(offset+5, m.m21()) + .put(offset+6, m.m02()) + .put(offset+7, m.m12()) + .put(offset+8, m.m22()); + } + + public void putTransposed(Matrix3f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m10()) + .putFloat(offset+8, m.m20()) + .putFloat(offset+12, m.m01()) + .putFloat(offset+16, m.m11()) + .putFloat(offset+20, m.m21()) + .putFloat(offset+24, m.m02()) + .putFloat(offset+28, m.m12()) + .putFloat(offset+32, m.m22()); + } + + public void putTransposed(Matrix2f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m01()) + .put(offset+3, m.m11()); + } + + public void putTransposed(Matrix2f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m10()) + .putFloat(offset+8, m.m01()) + .putFloat(offset+12, m.m11()); + } + + public void put(Matrix4d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, m.m03()) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, m.m12()) + .put(offset+7, m.m13()) + .put(offset+8, m.m20()) + .put(offset+9, m.m21()) + .put(offset+10, m.m22()) + .put(offset+11, m.m23()) + .put(offset+12, m.m30()) + .put(offset+13, m.m31()) + .put(offset+14, m.m32()) + .put(offset+15, m.m33()); + } + + public void put(Matrix4d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m01()) + .putDouble(offset+16, m.m02()) + .putDouble(offset+24, m.m03()) + .putDouble(offset+32, m.m10()) + .putDouble(offset+40, m.m11()) + .putDouble(offset+48, m.m12()) + .putDouble(offset+56, m.m13()) + .putDouble(offset+64, m.m20()) + .putDouble(offset+72, m.m21()) + .putDouble(offset+80, m.m22()) + .putDouble(offset+88, m.m23()) + .putDouble(offset+96, m.m30()) + .putDouble(offset+104, m.m31()) + .putDouble(offset+112, m.m32()) + .putDouble(offset+120, m.m33()); + } + + public void put(Matrix4x3d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, m.m10()) + .put(offset+4, m.m11()) + .put(offset+5, m.m12()) + .put(offset+6, m.m20()) + .put(offset+7, m.m21()) + .put(offset+8, m.m22()) + .put(offset+9, m.m30()) + .put(offset+10, m.m31()) + .put(offset+11, m.m32()); + } + + public void put(Matrix4x3d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m01()) + .putDouble(offset+16, m.m02()) + .putDouble(offset+24, m.m10()) + .putDouble(offset+32, m.m11()) + .putDouble(offset+40, m.m12()) + .putDouble(offset+48, m.m20()) + .putDouble(offset+56, m.m21()) + .putDouble(offset+64, m.m22()) + .putDouble(offset+72, m.m30()) + .putDouble(offset+80, m.m31()) + .putDouble(offset+88, m.m32()); + } + + public void putf(Matrix4d m, int offset, FloatBuffer dest) { + dest.put(offset, (float)m.m00()) + .put(offset+1, (float)m.m01()) + .put(offset+2, (float)m.m02()) + .put(offset+3, (float)m.m03()) + .put(offset+4, (float)m.m10()) + .put(offset+5, (float)m.m11()) + .put(offset+6, (float)m.m12()) + .put(offset+7, (float)m.m13()) + .put(offset+8, (float)m.m20()) + .put(offset+9, (float)m.m21()) + .put(offset+10, (float)m.m22()) + .put(offset+11, (float)m.m23()) + .put(offset+12, (float)m.m30()) + .put(offset+13, (float)m.m31()) + .put(offset+14, (float)m.m32()) + .put(offset+15, (float)m.m33()); + } + + public void putf(Matrix4d m, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float)m.m00()) + .putFloat(offset+4, (float)m.m01()) + .putFloat(offset+8, (float)m.m02()) + .putFloat(offset+12, (float)m.m03()) + .putFloat(offset+16, (float)m.m10()) + .putFloat(offset+20, (float)m.m11()) + .putFloat(offset+24, (float)m.m12()) + .putFloat(offset+28, (float)m.m13()) + .putFloat(offset+32, (float)m.m20()) + .putFloat(offset+36, (float)m.m21()) + .putFloat(offset+40, (float)m.m22()) + .putFloat(offset+44, (float)m.m23()) + .putFloat(offset+48, (float)m.m30()) + .putFloat(offset+52, (float)m.m31()) + .putFloat(offset+56, (float)m.m32()) + .putFloat(offset+60, (float)m.m33()); + } + + public void putf(Matrix4x3d m, int offset, FloatBuffer dest) { + dest.put(offset, (float)m.m00()) + .put(offset+1, (float)m.m01()) + .put(offset+2, (float)m.m02()) + .put(offset+3, (float)m.m10()) + .put(offset+4, (float)m.m11()) + .put(offset+5, (float)m.m12()) + .put(offset+6, (float)m.m20()) + .put(offset+7, (float)m.m21()) + .put(offset+8, (float)m.m22()) + .put(offset+9, (float)m.m30()) + .put(offset+10, (float)m.m31()) + .put(offset+11, (float)m.m32()); + } + + public void putf(Matrix4x3d m, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float)m.m00()) + .putFloat(offset+4, (float)m.m01()) + .putFloat(offset+8, (float)m.m02()) + .putFloat(offset+12, (float)m.m10()) + .putFloat(offset+16, (float)m.m11()) + .putFloat(offset+20, (float)m.m12()) + .putFloat(offset+24, (float)m.m20()) + .putFloat(offset+28, (float)m.m21()) + .putFloat(offset+32, (float)m.m22()) + .putFloat(offset+36, (float)m.m30()) + .putFloat(offset+40, (float)m.m31()) + .putFloat(offset+44, (float)m.m32()); + } + + public void putTransposed(Matrix4d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m20()) + .put(offset+3, m.m30()) + .put(offset+4, m.m01()) + .put(offset+5, m.m11()) + .put(offset+6, m.m21()) + .put(offset+7, m.m31()) + .put(offset+8, m.m02()) + .put(offset+9, m.m12()) + .put(offset+10, m.m22()) + .put(offset+11, m.m32()) + .put(offset+12, m.m03()) + .put(offset+13, m.m13()) + .put(offset+14, m.m23()) + .put(offset+15, m.m33()); + } + + public void putTransposed(Matrix4d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m10()) + .putDouble(offset+16, m.m20()) + .putDouble(offset+24, m.m30()) + .putDouble(offset+32, m.m01()) + .putDouble(offset+40, m.m11()) + .putDouble(offset+48, m.m21()) + .putDouble(offset+56, m.m31()) + .putDouble(offset+64, m.m02()) + .putDouble(offset+72, m.m12()) + .putDouble(offset+80, m.m22()) + .putDouble(offset+88, m.m32()) + .putDouble(offset+96, m.m03()) + .putDouble(offset+104, m.m13()) + .putDouble(offset+112, m.m23()) + .putDouble(offset+120, m.m33()); + } + + public void put4x3Transposed(Matrix4d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m20()) + .put(offset+3, m.m30()) + .put(offset+4, m.m01()) + .put(offset+5, m.m11()) + .put(offset+6, m.m21()) + .put(offset+7, m.m31()) + .put(offset+8, m.m02()) + .put(offset+9, m.m12()) + .put(offset+10, m.m22()) + .put(offset+11, m.m32()); + } + + public void put4x3Transposed(Matrix4d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m10()) + .putDouble(offset+16, m.m20()) + .putDouble(offset+24, m.m30()) + .putDouble(offset+32, m.m01()) + .putDouble(offset+40, m.m11()) + .putDouble(offset+48, m.m21()) + .putDouble(offset+56, m.m31()) + .putDouble(offset+64, m.m02()) + .putDouble(offset+72, m.m12()) + .putDouble(offset+80, m.m22()) + .putDouble(offset+88, m.m32()); + } + + public void putTransposed(Matrix4x3d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m20()) + .put(offset+3, m.m30()) + .put(offset+4, m.m01()) + .put(offset+5, m.m11()) + .put(offset+6, m.m21()) + .put(offset+7, m.m31()) + .put(offset+8, m.m02()) + .put(offset+9, m.m12()) + .put(offset+10, m.m22()) + .put(offset+11, m.m32()); + } + + public void putTransposed(Matrix4x3d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m10()) + .putDouble(offset+16, m.m20()) + .putDouble(offset+24, m.m30()) + .putDouble(offset+32, m.m01()) + .putDouble(offset+40, m.m11()) + .putDouble(offset+48, m.m21()) + .putDouble(offset+56, m.m31()) + .putDouble(offset+64, m.m02()) + .putDouble(offset+72, m.m12()) + .putDouble(offset+80, m.m22()) + .putDouble(offset+88, m.m32()); + } + + public void putTransposed(Matrix2d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m10()) + .put(offset+2, m.m01()) + .put(offset+3, m.m11()); + } + + public void putTransposed(Matrix2d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m10()) + .putDouble(offset+16, m.m01()) + .putDouble(offset+24, m.m11()); + } + + public void putfTransposed(Matrix4x3d m, int offset, FloatBuffer dest) { + dest.put(offset, (float)m.m00()) + .put(offset+1, (float)m.m10()) + .put(offset+2, (float)m.m20()) + .put(offset+3, (float)m.m30()) + .put(offset+4, (float)m.m01()) + .put(offset+5, (float)m.m11()) + .put(offset+6, (float)m.m21()) + .put(offset+7, (float)m.m31()) + .put(offset+8, (float)m.m02()) + .put(offset+9, (float)m.m12()) + .put(offset+10, (float)m.m22()) + .put(offset+11, (float)m.m32()); + } + + public void putfTransposed(Matrix4x3d m, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float)m.m00()) + .putFloat(offset+4, (float)m.m10()) + .putFloat(offset+8, (float)m.m20()) + .putFloat(offset+12, (float)m.m30()) + .putFloat(offset+16, (float)m.m01()) + .putFloat(offset+20, (float)m.m11()) + .putFloat(offset+24, (float)m.m21()) + .putFloat(offset+28, (float)m.m31()) + .putFloat(offset+32, (float)m.m02()) + .putFloat(offset+36, (float)m.m12()) + .putFloat(offset+40, (float)m.m22()) + .putFloat(offset+44, (float)m.m32()); + } + + public void putfTransposed(Matrix2d m, int offset, FloatBuffer dest) { + dest.put(offset, (float)m.m00()) + .put(offset+1, (float)m.m10()) + .put(offset+2, (float)m.m01()) + .put(offset+3, (float)m.m11()); + } + + public void putfTransposed(Matrix2d m, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float)m.m00()) + .putFloat(offset+4, (float)m.m10()) + .putFloat(offset+8, (float)m.m01()) + .putFloat(offset+12, (float)m.m11()); + } + + public void putfTransposed(Matrix4d m, int offset, FloatBuffer dest) { + dest.put(offset, (float)m.m00()) + .put(offset+1, (float)m.m10()) + .put(offset+2, (float)m.m20()) + .put(offset+3, (float)m.m30()) + .put(offset+4, (float)m.m01()) + .put(offset+5, (float)m.m11()) + .put(offset+6, (float)m.m21()) + .put(offset+7, (float)m.m31()) + .put(offset+8, (float)m.m02()) + .put(offset+9, (float)m.m12()) + .put(offset+10, (float)m.m22()) + .put(offset+11, (float)m.m32()) + .put(offset+12, (float)m.m03()) + .put(offset+13, (float)m.m13()) + .put(offset+14, (float)m.m23()) + .put(offset+15, (float)m.m33()); + } + + public void putfTransposed(Matrix4d m, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float)m.m00()) + .putFloat(offset+4, (float)m.m10()) + .putFloat(offset+8, (float)m.m20()) + .putFloat(offset+12, (float)m.m30()) + .putFloat(offset+16, (float)m.m01()) + .putFloat(offset+20, (float)m.m11()) + .putFloat(offset+24, (float)m.m21()) + .putFloat(offset+28, (float)m.m31()) + .putFloat(offset+32, (float)m.m02()) + .putFloat(offset+36, (float)m.m12()) + .putFloat(offset+40, (float)m.m22()) + .putFloat(offset+44, (float)m.m32()) + .putFloat(offset+48, (float)m.m03()) + .putFloat(offset+52, (float)m.m13()) + .putFloat(offset+56, (float)m.m23()) + .putFloat(offset+60, (float)m.m33()); + } + + public void put0(Matrix3f m, FloatBuffer dest) { + dest.put(0, m.m00()) + .put(1, m.m01()) + .put(2, m.m02()) + .put(3, m.m10()) + .put(4, m.m11()) + .put(5, m.m12()) + .put(6, m.m20()) + .put(7, m.m21()) + .put(8, m.m22()); + } + public void putN(Matrix3f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, m.m10()) + .put(offset+4, m.m11()) + .put(offset+5, m.m12()) + .put(offset+6, m.m20()) + .put(offset+7, m.m21()) + .put(offset+8, m.m22()); + } + public void put(Matrix3f m, int offset, FloatBuffer dest) { + if (offset == 0) + put0(m, dest); + else + putN(m, offset, dest); + } + + public void put0(Matrix3f m, ByteBuffer dest) { + dest.putFloat(0, m.m00()) + .putFloat(4, m.m01()) + .putFloat(8, m.m02()) + .putFloat(12, m.m10()) + .putFloat(16, m.m11()) + .putFloat(20, m.m12()) + .putFloat(24, m.m20()) + .putFloat(28, m.m21()) + .putFloat(32, m.m22()); + } + public void putN(Matrix3f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m02()) + .putFloat(offset+12, m.m10()) + .putFloat(offset+16, m.m11()) + .putFloat(offset+20, m.m12()) + .putFloat(offset+24, m.m20()) + .putFloat(offset+28, m.m21()) + .putFloat(offset+32, m.m22()); + } + public void put(Matrix3f m, int offset, ByteBuffer dest) { + if (offset == 0) + put0(m, dest); + else + putN(m, offset, dest); + } + + public void put3x4_0(Matrix3f m, ByteBuffer dest) { + dest.putFloat(0, m.m00()) + .putFloat(4, m.m01()) + .putFloat(8, m.m02()) + .putFloat(12, 0.0f) + .putFloat(16, m.m10()) + .putFloat(20, m.m11()) + .putFloat(24, m.m12()) + .putFloat(28, 0.0f) + .putFloat(32, m.m20()) + .putFloat(36, m.m21()) + .putFloat(40, m.m22()) + .putFloat(44, 0.0f); + } + private void put3x4_N(Matrix3f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m02()) + .putFloat(offset+12, 0.0f) + .putFloat(offset+16, m.m10()) + .putFloat(offset+20, m.m11()) + .putFloat(offset+24, m.m12()) + .putFloat(offset+28, 0.0f) + .putFloat(offset+32, m.m20()) + .putFloat(offset+36, m.m21()) + .putFloat(offset+40, m.m22()) + .putFloat(offset+44, 0.0f); + } + public void put3x4(Matrix3f m, int offset, ByteBuffer dest) { + if (offset == 0) + put3x4_0(m, dest); + else + put3x4_N(m, offset, dest); + } + + public void put3x4_0(Matrix3f m, FloatBuffer dest) { + dest.put(0, m.m00()) + .put(1, m.m01()) + .put(2, m.m02()) + .put(3, 0.0f) + .put(4, m.m10()) + .put(5, m.m11()) + .put(6, m.m12()) + .put(7, 0.0f) + .put(8, m.m20()) + .put(9, m.m21()) + .put(10, m.m22()) + .put(11, 0.0f); + } + public void put3x4_N(Matrix3f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, 0.0f) + .put(offset+4, m.m10()) + .put(offset+5, m.m11()) + .put(offset+6, m.m12()) + .put(offset+7, 0.0f) + .put(offset+8, m.m20()) + .put(offset+9, m.m21()) + .put(offset+10, m.m22()) + .put(offset+11, 0.0f); + } + public void put3x4(Matrix3f m, int offset, FloatBuffer dest) { + if (offset == 0) + put3x4_0(m, dest); + else + put3x4_N(m, offset, dest); + } + + public void put(Matrix3d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m02()) + .put(offset+3, m.m10()) + .put(offset+4, m.m11()) + .put(offset+5, m.m12()) + .put(offset+6, m.m20()) + .put(offset+7, m.m21()) + .put(offset+8, m.m22()); + } + + public void put(Matrix3d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m01()) + .putDouble(offset+16, m.m02()) + .putDouble(offset+24, m.m10()) + .putDouble(offset+32, m.m11()) + .putDouble(offset+40, m.m12()) + .putDouble(offset+48, m.m20()) + .putDouble(offset+56, m.m21()) + .putDouble(offset+64, m.m22()); + } + + public void put(Matrix3x2f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m10()) + .put(offset+3, m.m11()) + .put(offset+4, m.m20()) + .put(offset+5, m.m21()); + } + + public void put(Matrix3x2f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m10()) + .putFloat(offset+12, m.m11()) + .putFloat(offset+16, m.m20()) + .putFloat(offset+20, m.m21()); + } + + public void put(Matrix3x2d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m10()) + .put(offset+3, m.m11()) + .put(offset+4, m.m20()) + .put(offset+5, m.m21()); + } + + public void put(Matrix3x2d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m01()) + .putDouble(offset+16, m.m10()) + .putDouble(offset+24, m.m11()) + .putDouble(offset+32, m.m20()) + .putDouble(offset+40, m.m21()); + } + + public void putf(Matrix3d m, int offset, FloatBuffer dest) { + dest.put(offset, (float)m.m00()) + .put(offset+1, (float)m.m01()) + .put(offset+2, (float)m.m02()) + .put(offset+3, (float)m.m10()) + .put(offset+4, (float)m.m11()) + .put(offset+5, (float)m.m12()) + .put(offset+6, (float)m.m20()) + .put(offset+7, (float)m.m21()) + .put(offset+8, (float)m.m22()); + } + + public void put(Matrix2f m, int offset, FloatBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m10()) + .put(offset+3, m.m11()); + } + + public void put(Matrix2f m, int offset, ByteBuffer dest) { + dest.putFloat(offset, m.m00()) + .putFloat(offset+4, m.m01()) + .putFloat(offset+8, m.m10()) + .putFloat(offset+12, m.m11()); + } + + public void put(Matrix2d m, int offset, DoubleBuffer dest) { + dest.put(offset, m.m00()) + .put(offset+1, m.m01()) + .put(offset+2, m.m10()) + .put(offset+3, m.m11()); + } + + public void put(Matrix2d m, int offset, ByteBuffer dest) { + dest.putDouble(offset, m.m00()) + .putDouble(offset+8, m.m01()) + .putDouble(offset+16, m.m10()) + .putDouble(offset+24, m.m11()); + } + + public void putf(Matrix2d m, int offset, FloatBuffer dest) { + dest.put(offset, (float)m.m00()) + .put(offset+1, (float)m.m01()) + .put(offset+2, (float)m.m10()) + .put(offset+3, (float)m.m11()); + } + + public void putf(Matrix2d m, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float)m.m00()) + .putFloat(offset+4, (float)m.m01()) + .putFloat(offset+8, (float)m.m10()) + .putFloat(offset+12, (float)m.m11()); + } + + public void putf(Matrix3d m, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float)m.m00()) + .putFloat(offset+4, (float)m.m01()) + .putFloat(offset+8, (float)m.m02()) + .putFloat(offset+12, (float)m.m10()) + .putFloat(offset+16, (float)m.m11()) + .putFloat(offset+20, (float)m.m12()) + .putFloat(offset+24, (float)m.m20()) + .putFloat(offset+28, (float)m.m21()) + .putFloat(offset+32, (float)m.m22()); + } + + public void put(Vector4d src, int offset, DoubleBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y) + .put(offset+2, src.z) + .put(offset+3, src.w); + } + + public void put(Vector4d src, int offset, FloatBuffer dest) { + dest.put(offset, (float)src.x) + .put(offset+1, (float)src.y) + .put(offset+2, (float)src.z) + .put(offset+3, (float)src.w); + } + + public void put(Vector4d src, int offset, ByteBuffer dest) { + dest.putDouble(offset, src.x) + .putDouble(offset+8, src.y) + .putDouble(offset+16, src.z) + .putDouble(offset+24, src.w); + } + + public void putf(Vector4d src, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float) src.x) + .putFloat(offset+4, (float) src.y) + .putFloat(offset+8, (float) src.z) + .putFloat(offset+12, (float) src.w); + } + + public void put(Vector4f src, int offset, FloatBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y) + .put(offset+2, src.z) + .put(offset+3, src.w); + } + + public void put(Vector4f src, int offset, ByteBuffer dest) { + dest.putFloat(offset, src.x) + .putFloat(offset+4, src.y) + .putFloat(offset+8, src.z) + .putFloat(offset+12, src.w); + } + + public void put(Vector4i src, int offset, IntBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y) + .put(offset+2, src.z) + .put(offset+3, src.w); + } + + public void put(Vector4i src, int offset, ByteBuffer dest) { + dest.putInt(offset, src.x) + .putInt(offset+4, src.y) + .putInt(offset+8, src.z) + .putInt(offset+12, src.w); + } + + public void put(Vector3f src, int offset, FloatBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y) + .put(offset+2, src.z); + } + + public void put(Vector3f src, int offset, ByteBuffer dest) { + dest.putFloat(offset, src.x) + .putFloat(offset+4, src.y) + .putFloat(offset+8, src.z); + } + + public void put(Vector3d src, int offset, DoubleBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y) + .put(offset+2, src.z); + } + + public void put(Vector3d src, int offset, FloatBuffer dest) { + dest.put(offset, (float)src.x) + .put(offset+1, (float)src.y) + .put(offset+2, (float)src.z); + } + + public void put(Vector3d src, int offset, ByteBuffer dest) { + dest.putDouble(offset, src.x) + .putDouble(offset+8, src.y) + .putDouble(offset+16, src.z); + } + + public void putf(Vector3d src, int offset, ByteBuffer dest) { + dest.putFloat(offset, (float) src.x) + .putFloat(offset+4, (float) src.y) + .putFloat(offset+8, (float) src.z); + } + + public void put(Vector3i src, int offset, IntBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y) + .put(offset+2, src.z); + } + + public void put(Vector3i src, int offset, ByteBuffer dest) { + dest.putInt(offset, src.x) + .putInt(offset+4, src.y) + .putInt(offset+8, src.z); + } + + public void put(Vector2f src, int offset, FloatBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y); + } + + public void put(Vector2f src, int offset, ByteBuffer dest) { + dest.putFloat(offset, src.x) + .putFloat(offset+4, src.y); + } + + public void put(Vector2d src, int offset, DoubleBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y); + } + + public void put(Vector2d src, int offset, ByteBuffer dest) { + dest.putDouble(offset, src.x) + .putDouble(offset+8, src.y); + } + + public void put(Vector2i src, int offset, IntBuffer dest) { + dest.put(offset, src.x) + .put(offset+1, src.y); + } + + public void put(Vector2i src, int offset, ByteBuffer dest) { + dest.putInt(offset, src.x) + .putInt(offset+4, src.y); + } + + public void get(Matrix4f m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m03(src.get(offset+3)) + ._m10(src.get(offset+4)) + ._m11(src.get(offset+5)) + ._m12(src.get(offset+6)) + ._m13(src.get(offset+7)) + ._m20(src.get(offset+8)) + ._m21(src.get(offset+9)) + ._m22(src.get(offset+10)) + ._m23(src.get(offset+11)) + ._m30(src.get(offset+12)) + ._m31(src.get(offset+13)) + ._m32(src.get(offset+14)) + ._m33(src.get(offset+15)); + } + + public void get(Matrix4f m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m02(src.getFloat(offset+8)) + ._m03(src.getFloat(offset+12)) + ._m10(src.getFloat(offset+16)) + ._m11(src.getFloat(offset+20)) + ._m12(src.getFloat(offset+24)) + ._m13(src.getFloat(offset+28)) + ._m20(src.getFloat(offset+32)) + ._m21(src.getFloat(offset+36)) + ._m22(src.getFloat(offset+40)) + ._m23(src.getFloat(offset+44)) + ._m30(src.getFloat(offset+48)) + ._m31(src.getFloat(offset+52)) + ._m32(src.getFloat(offset+56)) + ._m33(src.getFloat(offset+60)); + } + + public void getTransposed(Matrix4f m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m10(src.get(offset+1)) + ._m20(src.get(offset+2)) + ._m30(src.get(offset+3)) + ._m01(src.get(offset+4)) + ._m11(src.get(offset+5)) + ._m21(src.get(offset+6)) + ._m31(src.get(offset+7)) + ._m02(src.get(offset+8)) + ._m12(src.get(offset+9)) + ._m22(src.get(offset+10)) + ._m32(src.get(offset+11)) + ._m03(src.get(offset+12)) + ._m13(src.get(offset+13)) + ._m23(src.get(offset+14)) + ._m33(src.get(offset+15)); + } + + public void getTransposed(Matrix4f m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m10(src.getFloat(offset+4)) + ._m20(src.getFloat(offset+8)) + ._m30(src.getFloat(offset+12)) + ._m01(src.getFloat(offset+16)) + ._m11(src.getFloat(offset+20)) + ._m21(src.getFloat(offset+24)) + ._m31(src.getFloat(offset+28)) + ._m02(src.getFloat(offset+32)) + ._m12(src.getFloat(offset+36)) + ._m22(src.getFloat(offset+40)) + ._m32(src.getFloat(offset+44)) + ._m03(src.getFloat(offset+48)) + ._m13(src.getFloat(offset+52)) + ._m23(src.getFloat(offset+56)) + ._m33(src.getFloat(offset+60)); + } + + public void get(Matrix4x3f m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m10(src.get(offset+3)) + ._m11(src.get(offset+4)) + ._m12(src.get(offset+5)) + ._m20(src.get(offset+6)) + ._m21(src.get(offset+7)) + ._m22(src.get(offset+8)) + ._m30(src.get(offset+9)) + ._m31(src.get(offset+10)) + ._m32(src.get(offset+11)); + } + + public void get(Matrix4x3f m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m02(src.getFloat(offset+8)) + ._m10(src.getFloat(offset+12)) + ._m11(src.getFloat(offset+16)) + ._m12(src.getFloat(offset+20)) + ._m20(src.getFloat(offset+24)) + ._m21(src.getFloat(offset+28)) + ._m22(src.getFloat(offset+32)) + ._m30(src.getFloat(offset+36)) + ._m31(src.getFloat(offset+40)) + ._m32(src.getFloat(offset+44)); + } + + public void get(Matrix4d m, int offset, DoubleBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m03(src.get(offset+3)) + ._m10(src.get(offset+4)) + ._m11(src.get(offset+5)) + ._m12(src.get(offset+6)) + ._m13(src.get(offset+7)) + ._m20(src.get(offset+8)) + ._m21(src.get(offset+9)) + ._m22(src.get(offset+10)) + ._m23(src.get(offset+11)) + ._m30(src.get(offset+12)) + ._m31(src.get(offset+13)) + ._m32(src.get(offset+14)) + ._m33(src.get(offset+15)); + } + + public void get(Matrix4d m, int offset, ByteBuffer src) { + m._m00(src.getDouble(offset)) + ._m01(src.getDouble(offset+8)) + ._m02(src.getDouble(offset+16)) + ._m03(src.getDouble(offset+24)) + ._m10(src.getDouble(offset+32)) + ._m11(src.getDouble(offset+40)) + ._m12(src.getDouble(offset+48)) + ._m13(src.getDouble(offset+56)) + ._m20(src.getDouble(offset+64)) + ._m21(src.getDouble(offset+72)) + ._m22(src.getDouble(offset+80)) + ._m23(src.getDouble(offset+88)) + ._m30(src.getDouble(offset+96)) + ._m31(src.getDouble(offset+104)) + ._m32(src.getDouble(offset+112)) + ._m33(src.getDouble(offset+120)); + } + + public void get(Matrix4x3d m, int offset, DoubleBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m10(src.get(offset+3)) + ._m11(src.get(offset+4)) + ._m12(src.get(offset+5)) + ._m20(src.get(offset+6)) + ._m21(src.get(offset+7)) + ._m22(src.get(offset+8)) + ._m30(src.get(offset+9)) + ._m31(src.get(offset+10)) + ._m32(src.get(offset+11)); + } + + public void get(Matrix4x3d m, int offset, ByteBuffer src) { + m._m00(src.getDouble(offset)) + ._m01(src.getDouble(offset+8)) + ._m02(src.getDouble(offset+16)) + ._m10(src.getDouble(offset+24)) + ._m11(src.getDouble(offset+32)) + ._m12(src.getDouble(offset+40)) + ._m20(src.getDouble(offset+48)) + ._m21(src.getDouble(offset+56)) + ._m22(src.getDouble(offset+64)) + ._m30(src.getDouble(offset+72)) + ._m31(src.getDouble(offset+80)) + ._m32(src.getDouble(offset+88)); + } + + public void getf(Matrix4d m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m03(src.get(offset+3)) + ._m10(src.get(offset+4)) + ._m11(src.get(offset+5)) + ._m12(src.get(offset+6)) + ._m13(src.get(offset+7)) + ._m20(src.get(offset+8)) + ._m21(src.get(offset+9)) + ._m22(src.get(offset+10)) + ._m23(src.get(offset+11)) + ._m30(src.get(offset+12)) + ._m31(src.get(offset+13)) + ._m32(src.get(offset+14)) + ._m33(src.get(offset+15)); + } + + public void getf(Matrix4d m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m02(src.getFloat(offset+8)) + ._m03(src.getFloat(offset+12)) + ._m10(src.getFloat(offset+16)) + ._m11(src.getFloat(offset+20)) + ._m12(src.getFloat(offset+24)) + ._m13(src.getFloat(offset+28)) + ._m20(src.getFloat(offset+32)) + ._m21(src.getFloat(offset+36)) + ._m22(src.getFloat(offset+40)) + ._m23(src.getFloat(offset+44)) + ._m30(src.getFloat(offset+48)) + ._m31(src.getFloat(offset+52)) + ._m32(src.getFloat(offset+56)) + ._m33(src.getFloat(offset+60)); + } + + public void getf(Matrix4x3d m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m10(src.get(offset+3)) + ._m11(src.get(offset+4)) + ._m12(src.get(offset+5)) + ._m20(src.get(offset+6)) + ._m21(src.get(offset+7)) + ._m22(src.get(offset+8)) + ._m30(src.get(offset+9)) + ._m31(src.get(offset+10)) + ._m32(src.get(offset+11)); + } + + public void getf(Matrix4x3d m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m02(src.getFloat(offset+8)) + ._m10(src.getFloat(offset+12)) + ._m11(src.getFloat(offset+16)) + ._m12(src.getFloat(offset+20)) + ._m20(src.getFloat(offset+24)) + ._m21(src.getFloat(offset+28)) + ._m22(src.getFloat(offset+32)) + ._m30(src.getFloat(offset+36)) + ._m31(src.getFloat(offset+40)) + ._m32(src.getFloat(offset+44)); + } + + public void get(Matrix3f m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m10(src.get(offset+3)) + ._m11(src.get(offset+4)) + ._m12(src.get(offset+5)) + ._m20(src.get(offset+6)) + ._m21(src.get(offset+7)) + ._m22(src.get(offset+8)); + } + + public void get(Matrix3f m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m02(src.getFloat(offset+8)) + ._m10(src.getFloat(offset+12)) + ._m11(src.getFloat(offset+16)) + ._m12(src.getFloat(offset+20)) + ._m20(src.getFloat(offset+24)) + ._m21(src.getFloat(offset+28)) + ._m22(src.getFloat(offset+32)); + } + + public void get(Matrix3d m, int offset, DoubleBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m10(src.get(offset+3)) + ._m11(src.get(offset+4)) + ._m12(src.get(offset+5)) + ._m20(src.get(offset+6)) + ._m21(src.get(offset+7)) + ._m22(src.get(offset+8)); + } + + public void get(Matrix3d m, int offset, ByteBuffer src) { + m._m00(src.getDouble(offset)) + ._m01(src.getDouble(offset+8)) + ._m02(src.getDouble(offset+16)) + ._m10(src.getDouble(offset+24)) + ._m11(src.getDouble(offset+32)) + ._m12(src.getDouble(offset+40)) + ._m20(src.getDouble(offset+48)) + ._m21(src.getDouble(offset+56)) + ._m22(src.getDouble(offset+64)); + } + + public void get(Matrix3x2f m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m10(src.get(offset+2)) + ._m11(src.get(offset+3)) + ._m20(src.get(offset+4)) + ._m21(src.get(offset+5)); + } + + public void get(Matrix3x2f m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m10(src.getFloat(offset+8)) + ._m11(src.getFloat(offset+12)) + ._m20(src.getFloat(offset+16)) + ._m21(src.getFloat(offset+20)); + } + + public void get(Matrix3x2d m, int offset, DoubleBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m10(src.get(offset+2)) + ._m11(src.get(offset+3)) + ._m20(src.get(offset+4)) + ._m21(src.get(offset+5)); + } + + public void get(Matrix3x2d m, int offset, ByteBuffer src) { + m._m00(src.getDouble(offset)) + ._m01(src.getDouble(offset+8)) + ._m10(src.getDouble(offset+16)) + ._m11(src.getDouble(offset+24)) + ._m20(src.getDouble(offset+32)) + ._m21(src.getDouble(offset+40)); + } + + public void getf(Matrix3d m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m02(src.get(offset+2)) + ._m10(src.get(offset+3)) + ._m11(src.get(offset+4)) + ._m12(src.get(offset+5)) + ._m20(src.get(offset+6)) + ._m21(src.get(offset+7)) + ._m22(src.get(offset+8)); + } + + public void getf(Matrix3d m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m02(src.getFloat(offset+8)) + ._m10(src.getFloat(offset+12)) + ._m11(src.getFloat(offset+16)) + ._m12(src.getFloat(offset+20)) + ._m20(src.getFloat(offset+24)) + ._m21(src.getFloat(offset+28)) + ._m22(src.getFloat(offset+32)); + } + + public void get(Matrix2f m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m10(src.get(offset+2)) + ._m11(src.get(offset+3)); + } + + public void get(Matrix2f m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m10(src.getFloat(offset+8)) + ._m11(src.getFloat(offset+12)); + } + + public void get(Matrix2d m, int offset, DoubleBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m10(src.get(offset+2)) + ._m11(src.get(offset+3)); + } + + public void get(Matrix2d m, int offset, ByteBuffer src) { + m._m00(src.getDouble(offset)) + ._m01(src.getDouble(offset+8)) + ._m10(src.getDouble(offset+16)) + ._m11(src.getDouble(offset+24)); + } + + public void getf(Matrix2d m, int offset, FloatBuffer src) { + m._m00(src.get(offset)) + ._m01(src.get(offset+1)) + ._m10(src.get(offset+2)) + ._m11(src.get(offset+3)); + } + + public void getf(Matrix2d m, int offset, ByteBuffer src) { + m._m00(src.getFloat(offset)) + ._m01(src.getFloat(offset+4)) + ._m10(src.getFloat(offset+8)) + ._m11(src.getFloat(offset+12)); + } + + public void get(Vector4d dst, int offset, DoubleBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + dst.z = src.get(offset+2); + dst.w = src.get(offset+3); + } + + public void get(Vector4d dst, int offset, ByteBuffer src) { + dst.x = src.getDouble(offset); + dst.y = src.getDouble(offset+8); + dst.z = src.getDouble(offset+16); + dst.w = src.getDouble(offset+24); + } + + public void get(Vector4f dst, int offset, FloatBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + dst.z = src.get(offset+2); + dst.w = src.get(offset+3); + } + + public void get(Vector4f dst, int offset, ByteBuffer src) { + dst.x = src.getFloat(offset); + dst.y = src.getFloat(offset+4); + dst.z = src.getFloat(offset+8); + dst.w = src.getFloat(offset+12); + } + + public void get(Vector4i dst, int offset, IntBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + dst.z = src.get(offset+2); + dst.w = src.get(offset+3); + } + + public void get(Vector4i dst, int offset, ByteBuffer src) { + dst.x = src.getInt(offset); + dst.y = src.getInt(offset+4); + dst.z = src.getInt(offset+8); + dst.w = src.getInt(offset+12); + } + + public void get(Vector3f dst, int offset, FloatBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + dst.z = src.get(offset+2); + } + + public void get(Vector3f dst, int offset, ByteBuffer src) { + dst.x = src.getFloat(offset); + dst.y = src.getFloat(offset+4); + dst.z = src.getFloat(offset+8); + } + + public void get(Vector3d dst, int offset, DoubleBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + dst.z = src.get(offset+2); + } + + public void get(Vector3d dst, int offset, ByteBuffer src) { + dst.x = src.getDouble(offset); + dst.y = src.getDouble(offset+8); + dst.z = src.getDouble(offset+16); + } + + public void get(Vector3i dst, int offset, IntBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + dst.z = src.get(offset+2); + } + + public void get(Vector3i dst, int offset, ByteBuffer src) { + dst.x = src.getInt(offset); + dst.y = src.getInt(offset+4); + dst.z = src.getInt(offset+8); + } + + public void get(Vector2f dst, int offset, FloatBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + } + + public void get(Vector2f dst, int offset, ByteBuffer src) { + dst.x = src.getFloat(offset); + dst.y = src.getFloat(offset+4); + } + + public void get(Vector2d dst, int offset, DoubleBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + } + + public void get(Vector2d dst, int offset, ByteBuffer src) { + dst.x = src.getDouble(offset); + dst.y = src.getDouble(offset+8); + } + + public void get(Vector2i dst, int offset, IntBuffer src) { + dst.x = src.get(offset); + dst.y = src.get(offset+1); + } + + public void get(Vector2i dst, int offset, ByteBuffer src) { + dst.x = src.getInt(offset); + dst.y = src.getInt(offset+4); + } + + public float get(Matrix4f m, int column, int row) { + switch (column) { + case 0: + switch (row) { + case 0: + return m.m00; + case 1: + return m.m01; + case 2: + return m.m02; + case 3: + return m.m03; + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m.m10; + case 1: + return m.m11; + case 2: + return m.m12; + case 3: + return m.m13; + default: + break; + } + break; + case 2: + switch (row) { + case 0: + return m.m20; + case 1: + return m.m21; + case 2: + return m.m22; + case 3: + return m.m23; + default: + break; + } + break; + case 3: + switch (row) { + case 0: + return m.m30; + case 1: + return m.m31; + case 2: + return m.m32; + case 3: + return m.m33; + default: + break; + } + break; + default: + break; + } + throw new IllegalArgumentException(); + } + + public Matrix4f set(Matrix4f m, int column, int row, float value) { + switch (column) { + case 0: + switch (row) { + case 0: + return m.m00(value); + case 1: + return m.m01(value); + case 2: + return m.m02(value); + case 3: + return m.m03(value); + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m.m10(value); + case 1: + return m.m11(value); + case 2: + return m.m12(value); + case 3: + return m.m13(value); + default: + break; + } + break; + case 2: + switch (row) { + case 0: + return m.m20(value); + case 1: + return m.m21(value); + case 2: + return m.m22(value); + case 3: + return m.m23(value); + default: + break; + } + break; + case 3: + switch (row) { + case 0: + return m.m30(value); + case 1: + return m.m31(value); + case 2: + return m.m32(value); + case 3: + return m.m33(value); + default: + break; + } + break; + default: + break; + } + throw new IllegalArgumentException(); + } + + public double get(Matrix4d m, int column, int row) { + switch (column) { + case 0: + switch (row) { + case 0: + return m.m00; + case 1: + return m.m01; + case 2: + return m.m02; + case 3: + return m.m03; + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m.m10; + case 1: + return m.m11; + case 2: + return m.m12; + case 3: + return m.m13; + default: + break; + } + break; + case 2: + switch (row) { + case 0: + return m.m20; + case 1: + return m.m21; + case 2: + return m.m22; + case 3: + return m.m23; + default: + break; + } + break; + case 3: + switch (row) { + case 0: + return m.m30; + case 1: + return m.m31; + case 2: + return m.m32; + case 3: + return m.m33; + default: + break; + } + break; + default: + break; + } + throw new IllegalArgumentException(); + } + + public Matrix4d set(Matrix4d m, int column, int row, double value) { + switch (column) { + case 0: + switch (row) { + case 0: + return m.m00(value); + case 1: + return m.m01(value); + case 2: + return m.m02(value); + case 3: + return m.m03(value); + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m.m10(value); + case 1: + return m.m11(value); + case 2: + return m.m12(value); + case 3: + return m.m13(value); + default: + break; + } + break; + case 2: + switch (row) { + case 0: + return m.m20(value); + case 1: + return m.m21(value); + case 2: + return m.m22(value); + case 3: + return m.m23(value); + default: + break; + } + break; + case 3: + switch (row) { + case 0: + return m.m30(value); + case 1: + return m.m31(value); + case 2: + return m.m32(value); + case 3: + return m.m33(value); + default: + break; + } + break; + default: + break; + } + throw new IllegalArgumentException(); + } + + public float get(Matrix3f m, int column, int row) { + switch (column) { + case 0: + switch (row) { + case 0: + return m.m00; + case 1: + return m.m01; + case 2: + return m.m02; + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m.m10; + case 1: + return m.m11; + case 2: + return m.m12; + default: + break; + } + break; + case 2: + switch (row) { + case 0: + return m.m20; + case 1: + return m.m21; + case 2: + return m.m22; + default: + break; + } + break; + default: + break; + } + throw new IllegalArgumentException(); + } + + public Matrix3f set(Matrix3f m, int column, int row, float value) { + switch (column) { + case 0: + switch (row) { + case 0: + return m.m00(value); + case 1: + return m.m01(value); + case 2: + return m.m02(value); + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m.m10(value); + case 1: + return m.m11(value); + case 2: + return m.m12(value); + default: + break; + } + break; + case 2: + switch (row) { + case 0: + return m.m20(value); + case 1: + return m.m21(value); + case 2: + return m.m22(value); + default: + break; + } + break; + default: + break; + } + throw new IllegalArgumentException(); + } + + public double get(Matrix3d m, int column, int row) { + switch (column) { + case 0: + switch (row) { + case 0: + return m.m00; + case 1: + return m.m01; + case 2: + return m.m02; + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m.m10; + case 1: + return m.m11; + case 2: + return m.m12; + default: + break; + } + break; + case 2: + switch (row) { + case 0: + return m.m20; + case 1: + return m.m21; + case 2: + return m.m22; + default: + break; + } + break; + default: + break; + } + throw new IllegalArgumentException(); + } + + public Matrix3d set(Matrix3d m, int column, int row, double value) { + switch (column) { + case 0: + switch (row) { + case 0: + return m.m00(value); + case 1: + return m.m01(value); + case 2: + return m.m02(value); + default: + break; + } + break; + case 1: + switch (row) { + case 0: + return m.m10(value); + case 1: + return m.m11(value); + case 2: + return m.m12(value); + default: + break; + } + break; + case 2: + switch (row) { + case 0: + return m.m20(value); + case 1: + return m.m21(value); + case 2: + return m.m22(value); + default: + break; + } + break; + default: + break; + } + throw new IllegalArgumentException(); + } + + public Vector4f getColumn(Matrix4f m, int column, Vector4f dest) { + switch (column) { + case 0: + return dest.set(m.m00, m.m01, m.m02, m.m03); + case 1: + return dest.set(m.m10, m.m11, m.m12, m.m13); + case 2: + return dest.set(m.m20, m.m21, m.m22, m.m23); + case 3: + return dest.set(m.m30, m.m31, m.m32, m.m33); + default: + throw new IndexOutOfBoundsException(); + } + } + + public Matrix4f setColumn(Vector4f v, int column, Matrix4f dest) { + switch (column) { + case 0: + return dest._m00(v.x)._m01(v.y)._m02(v.z)._m03(v.w); + case 1: + return dest._m10(v.x)._m11(v.y)._m12(v.z)._m13(v.w); + case 2: + return dest._m20(v.x)._m21(v.y)._m22(v.z)._m23(v.w); + case 3: + return dest._m30(v.x)._m31(v.y)._m32(v.z)._m33(v.w); + default: + throw new IndexOutOfBoundsException(); + } + } + + public Matrix4f setColumn(Vector4fc v, int column, Matrix4f dest) { + switch (column) { + case 0: + return dest._m00(v.x())._m01(v.y())._m02(v.z())._m03(v.w()); + case 1: + return dest._m10(v.x())._m11(v.y())._m12(v.z())._m13(v.w()); + case 2: + return dest._m20(v.x())._m21(v.y())._m22(v.z())._m23(v.w()); + case 3: + return dest._m30(v.x())._m31(v.y())._m32(v.z())._m33(v.w()); + default: + throw new IndexOutOfBoundsException(); + } + } + + public void copy(Matrix4f src, Matrix4f dest) { + dest._m00(src.m00()). + _m01(src.m01()). + _m02(src.m02()). + _m03(src.m03()). + _m10(src.m10()). + _m11(src.m11()). + _m12(src.m12()). + _m13(src.m13()). + _m20(src.m20()). + _m21(src.m21()). + _m22(src.m22()). + _m23(src.m23()). + _m30(src.m30()). + _m31(src.m31()). + _m32(src.m32()). + _m33(src.m33()); + } + + public void copy(Matrix3f src, Matrix4f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m03(0.0f) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m13(0.0f) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f); + } + + public void copy(Matrix4f src, Matrix3f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()); + } + + public void copy(Matrix3f src, Matrix4x3f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f); + } + + public void copy(Matrix3x2f src, Matrix3x2f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m20(src.m20()) + ._m21(src.m21()); + } + + public void copy(Matrix3x2d src, Matrix3x2d dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m20(src.m20()) + ._m21(src.m21()); + } + + public void copy(Matrix2f src, Matrix2f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()); + } + + public void copy(Matrix2d src, Matrix2d dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()); + } + + public void copy(Matrix2f src, Matrix3f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(0.0f) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(0.0f) + ._m20(0.0f) + ._m21(0.0f) + ._m22(1.0f); + } + + public void copy(Matrix3f src, Matrix2f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()); + } + + public void copy(Matrix2f src, Matrix3x2f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m20(0.0f) + ._m21(0.0f); + } + + public void copy(Matrix3x2f src, Matrix2f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()); + } + + public void copy(Matrix2d src, Matrix3d dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(0.0) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(0.0) + ._m20(0.0) + ._m21(0.0) + ._m22(1.0); + } + + public void copy(Matrix3d src, Matrix2d dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()); + } + + public void copy(Matrix2d src, Matrix3x2d dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m20(0.0) + ._m21(0.0); + } + + public void copy(Matrix3x2d src, Matrix2d dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m10(src.m10()) + ._m11(src.m11()); + } + + public void copy3x3(Matrix4f src, Matrix4f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()); + } + + public void copy3x3(Matrix4x3f src, Matrix4x3f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()); + } + + public void copy3x3(Matrix3f src, Matrix4x3f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()); + } + + public void copy3x3(Matrix3f src, Matrix4f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()); + } + + public void copy4x3(Matrix4x3f src, Matrix4f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()) + ._m30(src.m30()) + ._m31(src.m31()) + ._m32(src.m32()); + } + + public void copy4x3(Matrix4f src, Matrix4f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()) + ._m30(src.m30()) + ._m31(src.m31()) + ._m32(src.m32()); + } + + public void copy(Matrix4f src, Matrix4x3f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()) + ._m30(src.m30()) + ._m31(src.m31()) + ._m32(src.m32()); + } + + public void copy(Matrix4x3f src, Matrix4f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m03(0.0f) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m13(0.0f) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()) + ._m23(0.0f) + ._m30(src.m30()) + ._m31(src.m31()) + ._m32(src.m32()) + ._m33(1.0f); + } + + public void copy(Matrix4x3f src, Matrix4x3f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()) + ._m30(src.m30()) + ._m31(src.m31()) + ._m32(src.m32()); + } + + public void copy(Matrix3f src, Matrix3f dest) { + dest._m00(src.m00()) + ._m01(src.m01()) + ._m02(src.m02()) + ._m10(src.m10()) + ._m11(src.m11()) + ._m12(src.m12()) + ._m20(src.m20()) + ._m21(src.m21()) + ._m22(src.m22()); + } + + public void copy(float[] arr, int off, Matrix4f dest) { + dest._m00(arr[off+0]) + ._m01(arr[off+1]) + ._m02(arr[off+2]) + ._m03(arr[off+3]) + ._m10(arr[off+4]) + ._m11(arr[off+5]) + ._m12(arr[off+6]) + ._m13(arr[off+7]) + ._m20(arr[off+8]) + ._m21(arr[off+9]) + ._m22(arr[off+10]) + ._m23(arr[off+11]) + ._m30(arr[off+12]) + ._m31(arr[off+13]) + ._m32(arr[off+14]) + ._m33(arr[off+15]); + } + + public void copyTransposed(float[] arr, int off, Matrix4f dest) { + dest._m00(arr[off+0]) + ._m10(arr[off+1]) + ._m20(arr[off+2]) + ._m30(arr[off+3]) + ._m01(arr[off+4]) + ._m11(arr[off+5]) + ._m21(arr[off+6]) + ._m31(arr[off+7]) + ._m02(arr[off+8]) + ._m12(arr[off+9]) + ._m22(arr[off+10]) + ._m32(arr[off+11]) + ._m03(arr[off+12]) + ._m13(arr[off+13]) + ._m23(arr[off+14]) + ._m33(arr[off+15]); + } + + public void copy(float[] arr, int off, Matrix3f dest) { + dest._m00(arr[off+0]) + ._m01(arr[off+1]) + ._m02(arr[off+2]) + ._m10(arr[off+3]) + ._m11(arr[off+4]) + ._m12(arr[off+5]) + ._m20(arr[off+6]) + ._m21(arr[off+7]) + ._m22(arr[off+8]); + } + + public void copy(float[] arr, int off, Matrix4x3f dest) { + dest._m00(arr[off+0]) + ._m01(arr[off+1]) + ._m02(arr[off+2]) + ._m10(arr[off+3]) + ._m11(arr[off+4]) + ._m12(arr[off+5]) + ._m20(arr[off+6]) + ._m21(arr[off+7]) + ._m22(arr[off+8]) + ._m30(arr[off+9]) + ._m31(arr[off+10]) + ._m32(arr[off+11]); + } + + public void copy(float[] arr, int off, Matrix3x2f dest) { + dest._m00(arr[off+0]) + ._m01(arr[off+1]) + ._m10(arr[off+2]) + ._m11(arr[off+3]) + ._m20(arr[off+4]) + ._m21(arr[off+5]); + } + + public void copy(double[] arr, int off, Matrix3x2d dest) { + dest._m00(arr[off+0]) + ._m01(arr[off+1]) + ._m10(arr[off+2]) + ._m11(arr[off+3]) + ._m20(arr[off+4]) + ._m21(arr[off+5]); + } + + public void copy(float[] arr, int off, Matrix2f dest) { + dest._m00(arr[off+0]) + ._m01(arr[off+1]) + ._m10(arr[off+2]) + ._m11(arr[off+3]); + } + + public void copy(double[] arr, int off, Matrix2d dest) { + dest._m00(arr[off+0]) + ._m01(arr[off+1]) + ._m10(arr[off+2]) + ._m11(arr[off+3]); + } + + public void copy(Matrix4f src, float[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m02(); + dest[off+3] = src.m03(); + dest[off+4] = src.m10(); + dest[off+5] = src.m11(); + dest[off+6] = src.m12(); + dest[off+7] = src.m13(); + dest[off+8] = src.m20(); + dest[off+9] = src.m21(); + dest[off+10] = src.m22(); + dest[off+11] = src.m23(); + dest[off+12] = src.m30(); + dest[off+13] = src.m31(); + dest[off+14] = src.m32(); + dest[off+15] = src.m33(); + } + + public void copy(Matrix3f src, float[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m02(); + dest[off+3] = src.m10(); + dest[off+4] = src.m11(); + dest[off+5] = src.m12(); + dest[off+6] = src.m20(); + dest[off+7] = src.m21(); + dest[off+8] = src.m22(); + } + + public void copy(Matrix4x3f src, float[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m02(); + dest[off+3] = src.m10(); + dest[off+4] = src.m11(); + dest[off+5] = src.m12(); + dest[off+6] = src.m20(); + dest[off+7] = src.m21(); + dest[off+8] = src.m22(); + dest[off+9] = src.m30(); + dest[off+10] = src.m31(); + dest[off+11] = src.m32(); + } + + public void copy(Matrix3x2f src, float[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m10(); + dest[off+3] = src.m11(); + dest[off+4] = src.m20(); + dest[off+5] = src.m21(); + } + + public void copy(Matrix3x2d src, double[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m10(); + dest[off+3] = src.m11(); + dest[off+4] = src.m20(); + dest[off+5] = src.m21(); + } + + public void copy(Matrix2f src, float[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m10(); + dest[off+3] = src.m11(); + } + + public void copy(Matrix2d src, double[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m10(); + dest[off+3] = src.m11(); + } + + public void copy4x4(Matrix4x3f src, float[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m02(); + dest[off+3] = 0.0f; + dest[off+4] = src.m10(); + dest[off+5] = src.m11(); + dest[off+6] = src.m12(); + dest[off+7] = 0.0f; + dest[off+8] = src.m20(); + dest[off+9] = src.m21(); + dest[off+10] = src.m22(); + dest[off+11] = 0.0f; + dest[off+12] = src.m30(); + dest[off+13] = src.m31(); + dest[off+14] = src.m32(); + dest[off+15] = 1.0f; + } + + public void copy4x4(Matrix4x3d src, float[] dest, int off) { + dest[off+0] = (float) src.m00(); + dest[off+1] = (float) src.m01(); + dest[off+2] = (float) src.m02(); + dest[off+3] = 0.0f; + dest[off+4] = (float) src.m10(); + dest[off+5] = (float) src.m11(); + dest[off+6] = (float) src.m12(); + dest[off+7] = 0.0f; + dest[off+8] = (float) src.m20(); + dest[off+9] = (float) src.m21(); + dest[off+10] = (float) src.m22(); + dest[off+11] = 0.0f; + dest[off+12] = (float) src.m30(); + dest[off+13] = (float) src.m31(); + dest[off+14] = (float) src.m32(); + dest[off+15] = 1.0f; + } + + public void copy4x4(Matrix4x3d src, double[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = src.m02(); + dest[off+3] = 0.0; + dest[off+4] = src.m10(); + dest[off+5] = src.m11(); + dest[off+6] = src.m12(); + dest[off+7] = 0.0; + dest[off+8] = src.m20(); + dest[off+9] = src.m21(); + dest[off+10] = src.m22(); + dest[off+11] = 0.0; + dest[off+12] = src.m30(); + dest[off+13] = src.m31(); + dest[off+14] = src.m32(); + dest[off+15] = 1.0; + } + + public void copy3x3(Matrix3x2f src, float[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = 0.0f; + dest[off+3] = src.m10(); + dest[off+4] = src.m11(); + dest[off+5] = 0.0f; + dest[off+6] = src.m20(); + dest[off+7] = src.m21(); + dest[off+8] = 1.0f; + } + + public void copy3x3(Matrix3x2d src, double[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = 0.0; + dest[off+3] = src.m10(); + dest[off+4] = src.m11(); + dest[off+5] = 0.0; + dest[off+6] = src.m20(); + dest[off+7] = src.m21(); + dest[off+8] = 1.0; + } + + public void copy4x4(Matrix3x2f src, float[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = 0.0f; + dest[off+3] = 0.0f; + dest[off+4] = src.m10(); + dest[off+5] = src.m11(); + dest[off+6] = 0.0f; + dest[off+7] = 0.0f; + dest[off+8] = 0.0f; + dest[off+9] = 0.0f; + dest[off+10] = 1.0f; + dest[off+11] = 0.0f; + dest[off+12] = src.m20(); + dest[off+13] = src.m21(); + dest[off+14] = 0.0f; + dest[off+15] = 1.0f; + } + + public void copy4x4(Matrix3x2d src, double[] dest, int off) { + dest[off+0] = src.m00(); + dest[off+1] = src.m01(); + dest[off+2] = 0.0; + dest[off+3] = 0.0; + dest[off+4] = src.m10(); + dest[off+5] = src.m11(); + dest[off+6] = 0.0; + dest[off+7] = 0.0; + dest[off+8] = 0.0; + dest[off+9] = 0.0; + dest[off+10] = 1.0; + dest[off+11] = 0.0; + dest[off+12] = src.m20(); + dest[off+13] = src.m21(); + dest[off+14] = 0.0; + dest[off+15] = 1.0; + } + + public void identity(Matrix4f dest) { + dest._m00(1.0f) + ._m01(0.0f) + ._m02(0.0f) + ._m03(0.0f) + ._m10(0.0f) + ._m11(1.0f) + ._m12(0.0f) + ._m13(0.0f) + ._m20(0.0f) + ._m21(0.0f) + ._m22(1.0f) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(1.0f); + } + + public void identity(Matrix4x3f dest) { + dest._m00(1.0f) + ._m01(0.0f) + ._m02(0.0f) + ._m10(0.0f) + ._m11(1.0f) + ._m12(0.0f) + ._m20(0.0f) + ._m21(0.0f) + ._m22(1.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f); + } + + public void identity(Matrix3f dest) { + dest._m00(1.0f) + ._m01(0.0f) + ._m02(0.0f) + ._m10(0.0f) + ._m11(1.0f) + ._m12(0.0f) + ._m20(0.0f) + ._m21(0.0f) + ._m22(1.0f); + } + + public void identity(Matrix3x2f dest) { + dest._m00(1.0f) + ._m01(0.0f) + ._m10(0.0f) + ._m11(1.0f) + ._m20(0.0f) + ._m21(0.0f); + } + + public void identity(Matrix3x2d dest) { + dest._m00(1.0) + ._m01(0.0) + ._m10(0.0) + ._m11(1.0) + ._m20(0.0) + ._m21(0.0); + } + + public void identity(Matrix2f dest) { + dest._m00(1.0f) + ._m01(0.0f) + ._m10(0.0f) + ._m11(1.0f); + } + + public void swap(Matrix4f m1, Matrix4f m2) { + float tmp; + tmp = m1.m00(); m1._m00(m2.m00()); m2._m00(tmp); + tmp = m1.m01(); m1._m01(m2.m01()); m2._m01(tmp); + tmp = m1.m02(); m1._m02(m2.m02()); m2._m02(tmp); + tmp = m1.m03(); m1._m03(m2.m03()); m2._m03(tmp); + tmp = m1.m10(); m1._m10(m2.m10()); m2._m10(tmp); + tmp = m1.m11(); m1._m11(m2.m11()); m2._m11(tmp); + tmp = m1.m12(); m1._m12(m2.m12()); m2._m12(tmp); + tmp = m1.m13(); m1._m13(m2.m13()); m2._m13(tmp); + tmp = m1.m20(); m1._m20(m2.m20()); m2._m20(tmp); + tmp = m1.m21(); m1._m21(m2.m21()); m2._m21(tmp); + tmp = m1.m22(); m1._m22(m2.m22()); m2._m22(tmp); + tmp = m1.m23(); m1._m23(m2.m23()); m2._m23(tmp); + tmp = m1.m30(); m1._m30(m2.m30()); m2._m30(tmp); + tmp = m1.m31(); m1._m31(m2.m31()); m2._m31(tmp); + tmp = m1.m32(); m1._m32(m2.m32()); m2._m32(tmp); + tmp = m1.m33(); m1._m33(m2.m33()); m2._m33(tmp); + } + + public void swap(Matrix4x3f m1, Matrix4x3f m2) { + float tmp; + tmp = m1.m00(); m1._m00(m2.m00()); m2._m00(tmp); + tmp = m1.m01(); m1._m01(m2.m01()); m2._m01(tmp); + tmp = m1.m02(); m1._m02(m2.m02()); m2._m02(tmp); + tmp = m1.m10(); m1._m10(m2.m10()); m2._m10(tmp); + tmp = m1.m11(); m1._m11(m2.m11()); m2._m11(tmp); + tmp = m1.m12(); m1._m12(m2.m12()); m2._m12(tmp); + tmp = m1.m20(); m1._m20(m2.m20()); m2._m20(tmp); + tmp = m1.m21(); m1._m21(m2.m21()); m2._m21(tmp); + tmp = m1.m22(); m1._m22(m2.m22()); m2._m22(tmp); + tmp = m1.m30(); m1._m30(m2.m30()); m2._m30(tmp); + tmp = m1.m31(); m1._m31(m2.m31()); m2._m31(tmp); + tmp = m1.m32(); m1._m32(m2.m32()); m2._m32(tmp); + } + + public void swap(Matrix3f m1, Matrix3f m2) { + float tmp; + tmp = m1.m00(); m1._m00(m2.m00()); m2._m00(tmp); + tmp = m1.m01(); m1._m01(m2.m01()); m2._m01(tmp); + tmp = m1.m02(); m1._m02(m2.m02()); m2._m02(tmp); + tmp = m1.m10(); m1._m10(m2.m10()); m2._m10(tmp); + tmp = m1.m11(); m1._m11(m2.m11()); m2._m11(tmp); + tmp = m1.m12(); m1._m12(m2.m12()); m2._m12(tmp); + tmp = m1.m20(); m1._m20(m2.m20()); m2._m20(tmp); + tmp = m1.m21(); m1._m21(m2.m21()); m2._m21(tmp); + tmp = m1.m22(); m1._m22(m2.m22()); m2._m22(tmp); + } + + public void swap(Matrix2f m1, Matrix2f m2) { + float tmp; + tmp = m1.m00(); m1._m00(m2.m00()); m2._m00(tmp); + tmp = m1.m01(); m1._m00(m2.m01()); m2._m01(tmp); + tmp = m1.m10(); m1._m00(m2.m10()); m2._m10(tmp); + tmp = m1.m11(); m1._m00(m2.m11()); m2._m11(tmp); + } + + public void swap(Matrix2d m1, Matrix2d m2) { + double tmp; + tmp = m1.m00(); m1._m00(m2.m00()); m2._m00(tmp); + tmp = m1.m01(); m1._m00(m2.m01()); m2._m01(tmp); + tmp = m1.m10(); m1._m00(m2.m10()); m2._m10(tmp); + tmp = m1.m11(); m1._m00(m2.m11()); m2._m11(tmp); + } + + public void zero(Matrix4f dest) { + dest._m00(0.0f) + ._m01(0.0f) + ._m02(0.0f) + ._m03(0.0f) + ._m10(0.0f) + ._m11(0.0f) + ._m12(0.0f) + ._m13(0.0f) + ._m20(0.0f) + ._m21(0.0f) + ._m22(0.0f) + ._m23(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f) + ._m33(0.0f); + } + + public void zero(Matrix4x3f dest) { + dest._m00(0.0f) + ._m01(0.0f) + ._m02(0.0f) + ._m10(0.0f) + ._m11(0.0f) + ._m12(0.0f) + ._m20(0.0f) + ._m21(0.0f) + ._m22(0.0f) + ._m30(0.0f) + ._m31(0.0f) + ._m32(0.0f); + } + + public void zero(Matrix3f dest) { + dest._m00(0.0f) + ._m01(0.0f) + ._m02(0.0f) + ._m10(0.0f) + ._m11(0.0f) + ._m12(0.0f) + ._m20(0.0f) + ._m21(0.0f) + ._m22(0.0f); + } + + public void zero(Matrix3x2f dest) { + dest._m00(0.0f) + ._m01(0.0f) + ._m10(0.0f) + ._m11(0.0f) + ._m20(0.0f) + ._m21(0.0f); + } + + public void zero(Matrix3x2d dest) { + dest._m00(0.0) + ._m01(0.0) + ._m10(0.0) + ._m11(0.0) + ._m20(0.0) + ._m21(0.0); + } + + public void zero(Matrix2f dest) { + dest._m00(0.0f) + ._m01(0.0f) + ._m10(0.0f) + ._m11(0.0f); + } + + public void zero(Matrix2d dest) { + dest._m00(0.0) + ._m01(0.0) + ._m10(0.0) + ._m11(0.0); + } + + public void putMatrix3f(Quaternionf q, int position, ByteBuffer dest) { + float w2 = q.w * q.w; + float x2 = q.x * q.x; + float y2 = q.y * q.y; + float z2 = q.z * q.z; + float zw = q.z * q.w; + float xy = q.x * q.y; + float xz = q.x * q.z; + float yw = q.y * q.w; + float yz = q.y * q.z; + float xw = q.x * q.w; + dest.putFloat(position, w2 + x2 - z2 - y2) + .putFloat(position + 4, xy + zw + zw + xy) + .putFloat(position + 8, xz - yw + xz - yw) + .putFloat(position + 12, -zw + xy - zw + xy) + .putFloat(position + 16, y2 - z2 + w2 - x2) + .putFloat(position + 20, yz + yz + xw + xw) + .putFloat(position + 24, yw + xz + xz + yw) + .putFloat(position + 28, yz + yz - xw - xw) + .putFloat(position + 32, z2 - y2 - x2 + w2); + } + + public void putMatrix3f(Quaternionf q, int position, FloatBuffer dest) { + float w2 = q.w * q.w; + float x2 = q.x * q.x; + float y2 = q.y * q.y; + float z2 = q.z * q.z; + float zw = q.z * q.w; + float xy = q.x * q.y; + float xz = q.x * q.z; + float yw = q.y * q.w; + float yz = q.y * q.z; + float xw = q.x * q.w; + dest.put(position, w2 + x2 - z2 - y2) + .put(position + 1, xy + zw + zw + xy) + .put(position + 2, xz - yw + xz - yw) + .put(position + 3, -zw + xy - zw + xy) + .put(position + 4, y2 - z2 + w2 - x2) + .put(position + 5, yz + yz + xw + xw) + .put(position + 6, yw + xz + xz + yw) + .put(position + 7, yz + yz - xw - xw) + .put(position + 8, z2 - y2 - x2 + w2); + } + + public void putMatrix4f(Quaternionf q, int position, ByteBuffer dest) { + float w2 = q.w * q.w; + float x2 = q.x * q.x; + float y2 = q.y * q.y; + float z2 = q.z * q.z; + float zw = q.z * q.w; + float xy = q.x * q.y; + float xz = q.x * q.z; + float yw = q.y * q.w; + float yz = q.y * q.z; + float xw = q.x * q.w; + dest.putFloat(position, w2 + x2 - z2 - y2) + .putFloat(position + 4, xy + zw + zw + xy) + .putFloat(position + 8, xz - yw + xz - yw) + .putFloat(position + 12, 0.0f) + .putFloat(position + 16, -zw + xy - zw + xy) + .putFloat(position + 20, y2 - z2 + w2 - x2) + .putFloat(position + 24, yz + yz + xw + xw) + .putFloat(position + 28, 0.0f) + .putFloat(position + 32, yw + xz + xz + yw) + .putFloat(position + 36, yz + yz - xw - xw) + .putFloat(position + 40, z2 - y2 - x2 + w2) + .putFloat(position + 44, 0.0f) + .putLong(position + 48, 0L) + .putLong(position + 56, 0x3F80000000000000L); + } + + public void putMatrix4f(Quaternionf q, int position, FloatBuffer dest) { + float w2 = q.w * q.w; + float x2 = q.x * q.x; + float y2 = q.y * q.y; + float z2 = q.z * q.z; + float zw = q.z * q.w; + float xy = q.x * q.y; + float xz = q.x * q.z; + float yw = q.y * q.w; + float yz = q.y * q.z; + float xw = q.x * q.w; + dest.put(position, w2 + x2 - z2 - y2) + .put(position + 1, xy + zw + zw + xy) + .put(position + 2, xz - yw + xz - yw) + .put(position + 3, 0.0f) + .put(position + 4, -zw + xy - zw + xy) + .put(position + 5, y2 - z2 + w2 - x2) + .put(position + 6, yz + yz + xw + xw) + .put(position + 7, 0.0f) + .put(position + 8, yw + xz + xz + yw) + .put(position + 9, yz + yz - xw - xw) + .put(position + 10, z2 - y2 - x2 + w2) + .put(position + 11, 0.0f) + .put(position + 12, 0.0f) + .put(position + 13, 0.0f) + .put(position + 14, 0.0f) + .put(position + 15, 1.0f); + } + + public void putMatrix4x3f(Quaternionf q, int position, ByteBuffer dest) { + float w2 = q.w * q.w; + float x2 = q.x * q.x; + float y2 = q.y * q.y; + float z2 = q.z * q.z; + float zw = q.z * q.w; + float xy = q.x * q.y; + float xz = q.x * q.z; + float yw = q.y * q.w; + float yz = q.y * q.z; + float xw = q.x * q.w; + dest.putFloat(position, w2 + x2 - z2 - y2) + .putFloat(position + 4, xy + zw + zw + xy) + .putFloat(position + 8, xz - yw + xz - yw) + .putFloat(position + 12, -zw + xy - zw + xy) + .putFloat(position + 16, y2 - z2 + w2 - x2) + .putFloat(position + 20, yz + yz + xw + xw) + .putFloat(position + 24, yw + xz + xz + yw) + .putFloat(position + 28, yz + yz - xw - xw) + .putFloat(position + 32, z2 - y2 - x2 + w2) + .putLong(position + 36, 0L) + .putFloat(position + 44, 0.0f); + } + + public void putMatrix4x3f(Quaternionf q, int position, FloatBuffer dest) { + float w2 = q.w * q.w; + float x2 = q.x * q.x; + float y2 = q.y * q.y; + float z2 = q.z * q.z; + float zw = q.z * q.w; + float xy = q.x * q.y; + float xz = q.x * q.z; + float yw = q.y * q.w; + float yz = q.y * q.z; + float xw = q.x * q.w; + dest.put(position, w2 + x2 - z2 - y2) + .put(position + 1, xy + zw + zw + xy) + .put(position + 2, xz - yw + xz - yw) + .put(position + 3, -zw + xy - zw + xy) + .put(position + 4, y2 - z2 + w2 - x2) + .put(position + 5, yz + yz + xw + xw) + .put(position + 6, yw + xz + xz + yw) + .put(position + 7, yz + yz - xw - xw) + .put(position + 8, z2 - y2 - x2 + w2) + .put(position + 9, 0.0f) + .put(position + 10, 0.0f) + .put(position + 11, 0.0f); + } + } + + public static class MemUtilUnsafe extends MemUtilNIO { + public static final sun.misc.Unsafe UNSAFE; + + public static final long ADDRESS; + public static final long Matrix2f_m00; + public static final long Matrix3f_m00; + public static final long Matrix3d_m00; + public static final long Matrix4f_m00; + public static final long Matrix4d_m00; + public static final long Matrix4x3f_m00; + public static final long Matrix3x2f_m00; + public static final long Vector4f_x; + public static final long Vector4i_x; + public static final long Vector3f_x; + public static final long Vector3i_x; + public static final long Vector2f_x; + public static final long Vector2i_x; + public static final long Quaternionf_x; + public static final long floatArrayOffset; + + static { + UNSAFE = getUnsafeInstance(); + try { + ADDRESS = findBufferAddress(); + Matrix4f_m00 = checkMatrix4f(); + Matrix4d_m00 = checkMatrix4d(); + Matrix4x3f_m00 = checkMatrix4x3f(); + Matrix3f_m00 = checkMatrix3f(); + Matrix3d_m00 = checkMatrix3d(); + Matrix3x2f_m00 = checkMatrix3x2f(); + Matrix2f_m00 = checkMatrix2f(); + Vector4f_x = checkVector4f(); + Vector4i_x = checkVector4i(); + Vector3f_x = checkVector3f(); + Vector3i_x = checkVector3i(); + Vector2f_x = checkVector2f(); + Vector2i_x = checkVector2i(); + Quaternionf_x = checkQuaternionf(); + floatArrayOffset = UNSAFE.arrayBaseOffset(float[].class); + // Check if we can use object field offset/address put/get methods + sun.misc.Unsafe.class.getDeclaredMethod("getLong", new Class[] {Object.class, long.class}); + sun.misc.Unsafe.class.getDeclaredMethod("putLong", new Class[] {Object.class, long.class, long.class}); + } catch (NoSuchFieldException e) { + throw new UnsupportedOperationException(e); + } catch (NoSuchMethodException e) { + throw new UnsupportedOperationException(e); + } + } + + private static long findBufferAddress() { + try { + return UNSAFE.objectFieldOffset(getDeclaredField(Buffer.class, "address")); //$NON-NLS-1$ + } catch (Exception e) { + throw new UnsupportedOperationException(e); + } + } + + private static long checkMatrix4f() throws NoSuchFieldException, SecurityException { + Field f = Matrix4f.class.getDeclaredField("m00"); + long Matrix4f_m00 = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + for (int i = 1; i < 16; i++) { + int c = i >>> 2; + int r = i & 3; + f = Matrix4f.class.getDeclaredField("m" + c + r); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Matrix4f_m00 + (i << 2)) + throw new UnsupportedOperationException("Unexpected Matrix4f element offset"); + } + return Matrix4f_m00; + } + + private static long checkMatrix4d() throws NoSuchFieldException, SecurityException { + Field f = Matrix4d.class.getDeclaredField("m00"); + long Matrix4d_m00 = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + for (int i = 1; i < 16; i++) { + int c = i >>> 2; + int r = i & 3; + f = Matrix4d.class.getDeclaredField("m" + c + r); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Matrix4d_m00 + (i << 3)) + throw new UnsupportedOperationException("Unexpected Matrix4d element offset"); + } + return Matrix4d_m00; + } + + private static long checkMatrix4x3f() throws NoSuchFieldException, SecurityException { + Field f = Matrix4x3f.class.getDeclaredField("m00"); + long Matrix4x3f_m00 = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + for (int i = 1; i < 12; i++) { + int c = i / 3; + int r = i % 3; + f = Matrix4x3f.class.getDeclaredField("m" + c + r); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Matrix4x3f_m00 + (i << 2)) + throw new UnsupportedOperationException("Unexpected Matrix4x3f element offset"); + } + return Matrix4x3f_m00; + } + + private static long checkMatrix3f() throws NoSuchFieldException, SecurityException { + Field f = Matrix3f.class.getDeclaredField("m00"); + long Matrix3f_m00 = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + for (int i = 1; i < 9; i++) { + int c = i / 3; + int r = i % 3; + f = Matrix3f.class.getDeclaredField("m" + c + r); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Matrix3f_m00 + (i << 2)) + throw new UnsupportedOperationException("Unexpected Matrix3f element offset"); + } + return Matrix3f_m00; + } + + private static long checkMatrix3d() throws NoSuchFieldException, SecurityException { + Field f = Matrix3d.class.getDeclaredField("m00"); + long Matrix3d_m00 = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + for (int i = 1; i < 9; i++) { + int c = i / 3; + int r = i % 3; + f = Matrix3d.class.getDeclaredField("m" + c + r); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Matrix3d_m00 + (i << 3)) + throw new UnsupportedOperationException("Unexpected Matrix3d element offset"); + } + return Matrix3d_m00; + } + + private static long checkMatrix3x2f() throws NoSuchFieldException, SecurityException { + Field f = Matrix3x2f.class.getDeclaredField("m00"); + long Matrix3x2f_m00 = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + for (int i = 1; i < 6; i++) { + int c = i / 2; + int r = i % 2; + f = Matrix3x2f.class.getDeclaredField("m" + c + r); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Matrix3x2f_m00 + (i << 2)) + throw new UnsupportedOperationException("Unexpected Matrix3x2f element offset"); + } + return Matrix3x2f_m00; + } + + private static long checkMatrix2f() throws NoSuchFieldException, SecurityException { + Field f = Matrix2f.class.getDeclaredField("m00"); + long Matrix2f_m00 = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + for (int i = 1; i < 4; i++) { + int c = i / 2; + int r = i % 2; + f = Matrix2f.class.getDeclaredField("m" + c + r); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Matrix2f_m00 + (i << 2)) + throw new UnsupportedOperationException("Unexpected Matrix2f element offset"); + } + return Matrix2f_m00; + } + + private static long checkVector4f() throws NoSuchFieldException, SecurityException { + Field f = Vector4f.class.getDeclaredField("x"); + long Vector4f_x = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + String[] names = {"y", "z", "w"}; + for (int i = 1; i < 4; i++) { + f = Vector4f.class.getDeclaredField(names[i-1]); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Vector4f_x + (i << 2)) + throw new UnsupportedOperationException("Unexpected Vector4f element offset"); + } + return Vector4f_x; + } + + private static long checkVector4i() throws NoSuchFieldException, SecurityException { + Field f = Vector4i.class.getDeclaredField("x"); + long Vector4i_x = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + String[] names = {"y", "z", "w"}; + for (int i = 1; i < 4; i++) { + f = Vector4i.class.getDeclaredField(names[i-1]); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Vector4i_x + (i << 2)) + throw new UnsupportedOperationException("Unexpected Vector4i element offset"); + } + return Vector4i_x; + } + + private static long checkVector3f() throws NoSuchFieldException, SecurityException { + Field f = Vector3f.class.getDeclaredField("x"); + long Vector3f_x = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + String[] names = {"y", "z"}; + for (int i = 1; i < 3; i++) { + f = Vector3f.class.getDeclaredField(names[i-1]); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Vector3f_x + (i << 2)) + throw new UnsupportedOperationException("Unexpected Vector3f element offset"); + } + return Vector3f_x; + } + + private static long checkVector3i() throws NoSuchFieldException, SecurityException { + Field f = Vector3i.class.getDeclaredField("x"); + long Vector3i_x = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + String[] names = {"y", "z"}; + for (int i = 1; i < 3; i++) { + f = Vector3i.class.getDeclaredField(names[i-1]); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Vector3i_x + (i << 2)) + throw new UnsupportedOperationException("Unexpected Vector3i element offset"); + } + return Vector3i_x; + } + + private static long checkVector2f() throws NoSuchFieldException, SecurityException { + Field f = Vector2f.class.getDeclaredField("x"); + long Vector2f_x = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + f = Vector2f.class.getDeclaredField("y"); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Vector2f_x + (1 << 2)) + throw new UnsupportedOperationException("Unexpected Vector2f element offset"); + return Vector2f_x; + } + + private static long checkVector2i() throws NoSuchFieldException, SecurityException { + Field f = Vector2i.class.getDeclaredField("x"); + long Vector2i_x = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + f = Vector2i.class.getDeclaredField("y"); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Vector2i_x + (1 << 2)) + throw new UnsupportedOperationException("Unexpected Vector2i element offset"); + return Vector2i_x; + } + + private static long checkQuaternionf() throws NoSuchFieldException, SecurityException { + Field f = Quaternionf.class.getDeclaredField("x"); + long Quaternionf_x = UNSAFE.objectFieldOffset(f); + // Validate expected field offsets + String[] names = {"y", "z", "w"}; + for (int i = 1; i < 4; i++) { + f = Quaternionf.class.getDeclaredField(names[i-1]); + long offset = UNSAFE.objectFieldOffset(f); + if (offset != Quaternionf_x + (i << 2)) + throw new UnsupportedOperationException("Unexpected Quaternionf element offset"); + } + return Quaternionf_x; + } + + private static java.lang.reflect.Field getDeclaredField(Class root, String fieldName) throws NoSuchFieldException { + Class type = root; + do { + try { + java.lang.reflect.Field field = type.getDeclaredField(fieldName); + return field; + } catch (NoSuchFieldException e) { + type = type.getSuperclass(); + } catch (SecurityException e) { + type = type.getSuperclass(); + } + } while (type != null); + throw new NoSuchFieldException(fieldName + " does not exist in " + root.getName() + " or any of its superclasses."); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public static sun.misc.Unsafe getUnsafeInstance() throws SecurityException { + java.lang.reflect.Field[] fields = sun.misc.Unsafe.class.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + java.lang.reflect.Field field = fields[i]; + if (!field.getType().equals(sun.misc.Unsafe.class)) + continue; + int modifiers = field.getModifiers(); + if (!(java.lang.reflect.Modifier.isStatic(modifiers) && java.lang.reflect.Modifier.isFinal(modifiers))) + continue; + field.setAccessible(true); + try { + return (sun.misc.Unsafe) field.get(null); + } catch (IllegalAccessException e) { + /* Ignore */ + } + break; + } + throw new UnsupportedOperationException(); + } + + public static void put(Matrix4f m, long destAddr) { + for (int i = 0; i < 8; i++) { + UNSAFE.putLong(null, destAddr + (i << 3), UNSAFE.getLong(m, Matrix4f_m00 + (i << 3))); + } + } + + public static void put4x3(Matrix4f m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + for (int i = 0; i < 4; i++) { + u.putLong(null, destAddr + 12 * i, u.getLong(m, Matrix4f_m00 + (i << 4))); + } + u.putFloat(null, destAddr + 8, m.m02()); + u.putFloat(null, destAddr + 20, m.m12()); + u.putFloat(null, destAddr + 32, m.m22()); + u.putFloat(null, destAddr + 44, m.m32()); + } + + public static void put3x4(Matrix4f m, long destAddr) { + for (int i = 0; i < 6; i++) { + UNSAFE.putLong(null, destAddr + (i << 3), UNSAFE.getLong(m, Matrix4f_m00 + (i << 3))); + } + } + + public static void put(Matrix4x3f m, long destAddr) { + for (int i = 0; i < 6; i++) { + UNSAFE.putLong(null, destAddr + (i << 3), UNSAFE.getLong(m, Matrix4x3f_m00 + (i << 3))); + } + } + + public static void put4x4(Matrix4x3f m, long destAddr) { + for (int i = 0; i < 4; i++) { + UNSAFE.putLong(null, destAddr + (i << 4), UNSAFE.getLong(m, Matrix4x3f_m00 + 12 * i)); + long lng = UNSAFE.getInt(m, Matrix4x3f_m00 + 8 + 12 * i) & 0xFFFFFFFFL; + UNSAFE.putLong(null, destAddr + 8 + (i << 4), lng); + } + UNSAFE.putFloat(null, destAddr + 60, 1.0f); + } + + public static void put3x4(Matrix4x3f m, long destAddr) { + for (int i = 0; i < 3; i++) { + UNSAFE.putLong(null, destAddr + (i << 4), UNSAFE.getLong(m, Matrix4x3f_m00 + 12 * i)); + UNSAFE.putFloat(null, destAddr + (i << 4) + 8, UNSAFE.getFloat(m, Matrix4x3f_m00 + 8 + 12 * i)); + UNSAFE.putFloat(null, destAddr + (i << 4) + 12, 0.0f); + } + } + + public static void put4x4(Matrix4x3d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m01()); + u.putDouble(null, destAddr + 16, m.m02()); + u.putDouble(null, destAddr + 24, 0.0); + u.putDouble(null, destAddr + 32, m.m10()); + u.putDouble(null, destAddr + 40, m.m11()); + u.putDouble(null, destAddr + 48, m.m12()); + u.putDouble(null, destAddr + 56, 0.0); + u.putDouble(null, destAddr + 64, m.m20()); + u.putDouble(null, destAddr + 72, m.m21()); + u.putDouble(null, destAddr + 80, m.m22()); + u.putDouble(null, destAddr + 88, 0.0); + u.putDouble(null, destAddr + 96, m.m30()); + u.putDouble(null, destAddr + 104, m.m31()); + u.putDouble(null, destAddr + 112, m.m32()); + u.putDouble(null, destAddr + 120, 1.0); + } + + public static void put4x4(Matrix3x2f m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putLong(null, destAddr, u.getLong(m, Matrix3x2f_m00)); + u.putLong(null, destAddr+8, 0L); + u.putLong(null, destAddr+16, u.getLong(m, Matrix3x2f_m00+8)); + u.putLong(null, destAddr+24, 0L); + u.putLong(null, destAddr+32, 0L); + u.putLong(null, destAddr+40, 0x3F800000L); + u.putLong(null, destAddr+48, u.getLong(m, Matrix3x2f_m00+16)); + u.putLong(null, destAddr+56, 0x3F80000000000000L); + } + + public static void put4x4(Matrix3x2d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m01()); + u.putDouble(null, destAddr + 16, 0.0); + u.putDouble(null, destAddr + 24, 0.0); + u.putDouble(null, destAddr + 32, m.m10()); + u.putDouble(null, destAddr + 40, m.m11()); + u.putDouble(null, destAddr + 48, 0.0); + u.putDouble(null, destAddr + 56, 0.0); + u.putDouble(null, destAddr + 64, 0.0); + u.putDouble(null, destAddr + 72, 0.0); + u.putDouble(null, destAddr + 80, 1.0); + u.putDouble(null, destAddr + 88, 0.0); + u.putDouble(null, destAddr + 96, m.m20()); + u.putDouble(null, destAddr + 104, m.m21()); + u.putDouble(null, destAddr + 112, 0.0); + u.putDouble(null, destAddr + 120, 1.0); + } + + public static void put3x3(Matrix3x2f m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putLong( null, destAddr, u.getLong(m, Matrix3x2f_m00)); + u.putInt( null, destAddr+8, 0); + u.putLong( null, destAddr+12, u.getLong(m, Matrix3x2f_m00+8)); + u.putInt( null, destAddr+20, 0); + u.putLong( null, destAddr+24, u.getLong(m, Matrix3x2f_m00+16)); + u.putFloat(null, destAddr+32, 1.0f); + } + + public static void put3x3(Matrix3x2d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m01()); + u.putDouble(null, destAddr + 16, 0.0); + u.putDouble(null, destAddr + 24, m.m10()); + u.putDouble(null, destAddr + 32, m.m11()); + u.putDouble(null, destAddr + 40, 0.0); + u.putDouble(null, destAddr + 48, m.m20()); + u.putDouble(null, destAddr + 56, m.m21()); + u.putDouble(null, destAddr + 64, 1.0); + } + + public static void putTransposed(Matrix4f m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, m.m00()); + u.putFloat(null, destAddr + 4, m.m10()); + u.putFloat(null, destAddr + 8, m.m20()); + u.putFloat(null, destAddr + 12, m.m30()); + u.putFloat(null, destAddr + 16, m.m01()); + u.putFloat(null, destAddr + 20, m.m11()); + u.putFloat(null, destAddr + 24, m.m21()); + u.putFloat(null, destAddr + 28, m.m31()); + u.putFloat(null, destAddr + 32, m.m02()); + u.putFloat(null, destAddr + 36, m.m12()); + u.putFloat(null, destAddr + 40, m.m22()); + u.putFloat(null, destAddr + 44, m.m32()); + u.putFloat(null, destAddr + 48, m.m03()); + u.putFloat(null, destAddr + 52, m.m13()); + u.putFloat(null, destAddr + 56, m.m23()); + u.putFloat(null, destAddr + 60, m.m33()); + } + + public static void put4x3Transposed(Matrix4f m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, m.m00()); + u.putFloat(null, destAddr + 4, m.m10()); + u.putFloat(null, destAddr + 8, m.m20()); + u.putFloat(null, destAddr + 12, m.m30()); + u.putFloat(null, destAddr + 16, m.m01()); + u.putFloat(null, destAddr + 20, m.m11()); + u.putFloat(null, destAddr + 24, m.m21()); + u.putFloat(null, destAddr + 28, m.m31()); + u.putFloat(null, destAddr + 32, m.m02()); + u.putFloat(null, destAddr + 36, m.m12()); + u.putFloat(null, destAddr + 40, m.m22()); + u.putFloat(null, destAddr + 44, m.m32()); + } + + public static void putTransposed(Matrix4x3f m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, m.m00()); + u.putFloat(null, destAddr + 4, m.m10()); + u.putFloat(null, destAddr + 8, m.m20()); + u.putFloat(null, destAddr + 12, m.m30()); + u.putFloat(null, destAddr + 16, m.m01()); + u.putFloat(null, destAddr + 20, m.m11()); + u.putFloat(null, destAddr + 24, m.m21()); + u.putFloat(null, destAddr + 28, m.m31()); + u.putFloat(null, destAddr + 32, m.m02()); + u.putFloat(null, destAddr + 36, m.m12()); + u.putFloat(null, destAddr + 40, m.m22()); + u.putFloat(null, destAddr + 44, m.m32()); + } + + public static void putTransposed(Matrix3f m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, m.m00()); + u.putFloat(null, destAddr + 4, m.m10()); + u.putFloat(null, destAddr + 8, m.m20()); + u.putFloat(null, destAddr + 12, m.m01()); + u.putFloat(null, destAddr + 16, m.m11()); + u.putFloat(null, destAddr + 20, m.m21()); + u.putFloat(null, destAddr + 24, m.m02()); + u.putFloat(null, destAddr + 28, m.m12()); + u.putFloat(null, destAddr + 32, m.m22()); + } + + public static void putTransposed(Matrix2f m, long destAddr) { + UNSAFE.putFloat(null, destAddr, m.m00()); + UNSAFE.putFloat(null, destAddr + 4, m.m10()); + UNSAFE.putFloat(null, destAddr + 8, m.m01()); + UNSAFE.putFloat(null, destAddr + 12, m.m11()); + } + + public static void put(Matrix4d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m01()); + u.putDouble(null, destAddr + 16, m.m02()); + u.putDouble(null, destAddr + 24, m.m03()); + u.putDouble(null, destAddr + 32, m.m10()); + u.putDouble(null, destAddr + 40, m.m11()); + u.putDouble(null, destAddr + 48, m.m12()); + u.putDouble(null, destAddr + 56, m.m13()); + u.putDouble(null, destAddr + 64, m.m20()); + u.putDouble(null, destAddr + 72, m.m21()); + u.putDouble(null, destAddr + 80, m.m22()); + u.putDouble(null, destAddr + 88, m.m23()); + u.putDouble(null, destAddr + 96, m.m30()); + u.putDouble(null, destAddr + 104, m.m31()); + u.putDouble(null, destAddr + 112, m.m32()); + u.putDouble(null, destAddr + 120, m.m33()); + } + + public static void put(Matrix4x3d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m01()); + u.putDouble(null, destAddr + 16, m.m02()); + u.putDouble(null, destAddr + 24, m.m10()); + u.putDouble(null, destAddr + 32, m.m11()); + u.putDouble(null, destAddr + 40, m.m12()); + u.putDouble(null, destAddr + 48, m.m20()); + u.putDouble(null, destAddr + 56, m.m21()); + u.putDouble(null, destAddr + 64, m.m22()); + u.putDouble(null, destAddr + 72, m.m30()); + u.putDouble(null, destAddr + 80, m.m31()); + u.putDouble(null, destAddr + 88, m.m32()); + } + + public static void putTransposed(Matrix4d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m10()); + u.putDouble(null, destAddr + 16, m.m20()); + u.putDouble(null, destAddr + 24, m.m30()); + u.putDouble(null, destAddr + 32, m.m01()); + u.putDouble(null, destAddr + 40, m.m11()); + u.putDouble(null, destAddr + 48, m.m21()); + u.putDouble(null, destAddr + 56, m.m31()); + u.putDouble(null, destAddr + 64, m.m02()); + u.putDouble(null, destAddr + 72, m.m12()); + u.putDouble(null, destAddr + 80, m.m22()); + u.putDouble(null, destAddr + 88, m.m32()); + u.putDouble(null, destAddr + 96, m.m03()); + u.putDouble(null, destAddr + 104, m.m13()); + u.putDouble(null, destAddr + 112, m.m23()); + u.putDouble(null, destAddr + 120, m.m33()); + } + + public static void putfTransposed(Matrix4d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, (float)m.m00()); + u.putFloat(null, destAddr + 4, (float)m.m10()); + u.putFloat(null, destAddr + 8, (float)m.m20()); + u.putFloat(null, destAddr + 12, (float)m.m30()); + u.putFloat(null, destAddr + 16, (float)m.m01()); + u.putFloat(null, destAddr + 20, (float)m.m11()); + u.putFloat(null, destAddr + 24, (float)m.m21()); + u.putFloat(null, destAddr + 28, (float)m.m31()); + u.putFloat(null, destAddr + 32, (float)m.m02()); + u.putFloat(null, destAddr + 36, (float)m.m12()); + u.putFloat(null, destAddr + 40, (float)m.m22()); + u.putFloat(null, destAddr + 44, (float)m.m32()); + u.putFloat(null, destAddr + 48, (float)m.m03()); + u.putFloat(null, destAddr + 52, (float)m.m13()); + u.putFloat(null, destAddr + 56, (float)m.m23()); + u.putFloat(null, destAddr + 60, (float)m.m33()); + } + + public static void put4x3Transposed(Matrix4d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m10()); + u.putDouble(null, destAddr + 16, m.m20()); + u.putDouble(null, destAddr + 24, m.m30()); + u.putDouble(null, destAddr + 32, m.m01()); + u.putDouble(null, destAddr + 40, m.m11()); + u.putDouble(null, destAddr + 48, m.m21()); + u.putDouble(null, destAddr + 56, m.m31()); + u.putDouble(null, destAddr + 64, m.m02()); + u.putDouble(null, destAddr + 72, m.m12()); + u.putDouble(null, destAddr + 80, m.m22()); + u.putDouble(null, destAddr + 88, m.m32()); + } + + public static void putTransposed(Matrix4x3d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m10()); + u.putDouble(null, destAddr + 16, m.m20()); + u.putDouble(null, destAddr + 24, m.m30()); + u.putDouble(null, destAddr + 32, m.m01()); + u.putDouble(null, destAddr + 40, m.m11()); + u.putDouble(null, destAddr + 48, m.m21()); + u.putDouble(null, destAddr + 56, m.m31()); + u.putDouble(null, destAddr + 64, m.m02()); + u.putDouble(null, destAddr + 72, m.m12()); + u.putDouble(null, destAddr + 80, m.m22()); + u.putDouble(null, destAddr + 88, m.m32()); + } + + public static void putTransposed(Matrix2d m, long destAddr) { + UNSAFE.putDouble(null, destAddr, m.m00()); + UNSAFE.putDouble(null, destAddr + 8, m.m10()); + UNSAFE.putDouble(null, destAddr + 16, m.m10()); + UNSAFE.putDouble(null, destAddr + 24, m.m10()); + } + + public static void putfTransposed(Matrix4x3d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, (float)m.m00()); + u.putFloat(null, destAddr + 4, (float)m.m10()); + u.putFloat(null, destAddr + 8, (float)m.m20()); + u.putFloat(null, destAddr + 12, (float)m.m30()); + u.putFloat(null, destAddr + 16, (float)m.m01()); + u.putFloat(null, destAddr + 20, (float)m.m11()); + u.putFloat(null, destAddr + 24, (float)m.m21()); + u.putFloat(null, destAddr + 28, (float)m.m31()); + u.putFloat(null, destAddr + 32, (float)m.m02()); + u.putFloat(null, destAddr + 36, (float)m.m12()); + u.putFloat(null, destAddr + 40, (float)m.m22()); + u.putFloat(null, destAddr + 44, (float)m.m32()); + } + + public static void putfTransposed(Matrix2d m, long destAddr) { + UNSAFE.putFloat(null, destAddr, (float)m.m00()); + UNSAFE.putFloat(null, destAddr + 4, (float)m.m00()); + UNSAFE.putFloat(null, destAddr + 8, (float)m.m00()); + UNSAFE.putFloat(null, destAddr + 12, (float)m.m00()); + } + + public static void putf(Matrix4d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, (float)m.m00()); + u.putFloat(null, destAddr + 4, (float)m.m01()); + u.putFloat(null, destAddr + 8, (float)m.m02()); + u.putFloat(null, destAddr + 12, (float)m.m03()); + u.putFloat(null, destAddr + 16, (float)m.m10()); + u.putFloat(null, destAddr + 20, (float)m.m11()); + u.putFloat(null, destAddr + 24, (float)m.m12()); + u.putFloat(null, destAddr + 28, (float)m.m13()); + u.putFloat(null, destAddr + 32, (float)m.m20()); + u.putFloat(null, destAddr + 36, (float)m.m21()); + u.putFloat(null, destAddr + 40, (float)m.m22()); + u.putFloat(null, destAddr + 44, (float)m.m23()); + u.putFloat(null, destAddr + 48, (float)m.m30()); + u.putFloat(null, destAddr + 52, (float)m.m31()); + u.putFloat(null, destAddr + 56, (float)m.m32()); + u.putFloat(null, destAddr + 60, (float)m.m33()); + } + + public static void putf(Matrix4x3d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, (float)m.m00()); + u.putFloat(null, destAddr + 4, (float)m.m01()); + u.putFloat(null, destAddr + 8, (float)m.m02()); + u.putFloat(null, destAddr + 12, (float)m.m10()); + u.putFloat(null, destAddr + 16, (float)m.m11()); + u.putFloat(null, destAddr + 20, (float)m.m12()); + u.putFloat(null, destAddr + 24, (float)m.m20()); + u.putFloat(null, destAddr + 28, (float)m.m21()); + u.putFloat(null, destAddr + 32, (float)m.m22()); + u.putFloat(null, destAddr + 36, (float)m.m30()); + u.putFloat(null, destAddr + 40, (float)m.m31()); + u.putFloat(null, destAddr + 44, (float)m.m32()); + } + + public static void put(Matrix3f m, long destAddr) { + for (int i = 0; i < 4; i++) { + UNSAFE.putLong(null, destAddr + (i << 3), UNSAFE.getLong(m, Matrix3f_m00 + (i << 3))); + } + UNSAFE.putFloat(null, destAddr + 32, m.m22()); + } + + public static void put3x4(Matrix3f m, long destAddr) { + for (int i = 0; i < 3; i++) { + UNSAFE.putLong(null, destAddr + (i << 4), UNSAFE.getLong(m, Matrix3f_m00 + 12 * i)); + UNSAFE.putFloat(null, destAddr + (i << 4) + 8, UNSAFE.getFloat(m, Matrix3f_m00 + 8 + 12 * i)); + UNSAFE.putFloat(null, destAddr + 12 * i, 0.0f); + } + } + + public static void put(Matrix3d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m01()); + u.putDouble(null, destAddr + 16, m.m02()); + u.putDouble(null, destAddr + 24, m.m10()); + u.putDouble(null, destAddr + 32, m.m11()); + u.putDouble(null, destAddr + 40, m.m12()); + u.putDouble(null, destAddr + 48, m.m20()); + u.putDouble(null, destAddr + 56, m.m21()); + u.putDouble(null, destAddr + 64, m.m22()); + } + + public static void put(Matrix3x2f m, long destAddr) { + for (int i = 0; i < 3; i++) { + UNSAFE.putLong(null, destAddr + (i << 3), UNSAFE.getLong(m, Matrix3x2f_m00 + (i << 3))); + } + } + + public static void put(Matrix3x2d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putDouble(null, destAddr, m.m00()); + u.putDouble(null, destAddr + 8, m.m01()); + u.putDouble(null, destAddr + 16, m.m10()); + u.putDouble(null, destAddr + 24, m.m11()); + u.putDouble(null, destAddr + 32, m.m20()); + u.putDouble(null, destAddr + 40, m.m21()); + } + + public static void putf(Matrix3d m, long destAddr) { + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, destAddr, (float)m.m00()); + u.putFloat(null, destAddr + 4, (float)m.m01()); + u.putFloat(null, destAddr + 8, (float)m.m02()); + u.putFloat(null, destAddr + 12, (float)m.m10()); + u.putFloat(null, destAddr + 16, (float)m.m11()); + u.putFloat(null, destAddr + 20, (float)m.m12()); + u.putFloat(null, destAddr + 24, (float)m.m20()); + u.putFloat(null, destAddr + 28, (float)m.m21()); + u.putFloat(null, destAddr + 32, (float)m.m22()); + } + + public static void put(Matrix2f m, long destAddr) { + UNSAFE.putLong(null, destAddr, UNSAFE.getLong(m, Matrix2f_m00)); + UNSAFE.putLong(null, destAddr + 8, UNSAFE.getLong(m, Matrix2f_m00 + 8)); + } + + public static void put(Matrix2d m, long destAddr) { + UNSAFE.putDouble(null, destAddr, m.m00()); + UNSAFE.putDouble(null, destAddr + 8, m.m01()); + UNSAFE.putDouble(null, destAddr + 16, m.m10()); + UNSAFE.putDouble(null, destAddr + 24, m.m11()); + } + + public static void putf(Matrix2d m, long destAddr) { + UNSAFE.putFloat(null, destAddr, (float)m.m00()); + UNSAFE.putFloat(null, destAddr + 4, (float)m.m01()); + UNSAFE.putFloat(null, destAddr + 8, (float)m.m10()); + UNSAFE.putFloat(null, destAddr + 12, (float)m.m11()); + } + + public static void put(Vector4d src, long destAddr) { + UNSAFE.putDouble(null, destAddr, src.x); + UNSAFE.putDouble(null, destAddr+8, src.y); + UNSAFE.putDouble(null, destAddr+16, src.z); + UNSAFE.putDouble(null, destAddr+24, src.w); + } + + public static void putf(Vector4d src, long destAddr) { + UNSAFE.putFloat(null, destAddr, (float) src.x); + UNSAFE.putFloat(null, destAddr+4, (float) src.y); + UNSAFE.putFloat(null, destAddr+8, (float) src.z); + UNSAFE.putFloat(null, destAddr+12, (float) src.w); + } + + public static void put(Vector4f src, long destAddr) { + UNSAFE.putLong(null, destAddr, UNSAFE.getLong(src, Vector4f_x)); + UNSAFE.putLong(null, destAddr+8, UNSAFE.getLong(src, Vector4f_x+8)); + } + + public static void put(Vector4i src, long destAddr) { + UNSAFE.putLong(null, destAddr, UNSAFE.getLong(src, Vector4i_x)); + UNSAFE.putLong(null, destAddr+8, UNSAFE.getLong(src, Vector4i_x+8)); + } + + public static void put(Vector3f src, long destAddr) { + UNSAFE.putLong(null, destAddr, UNSAFE.getLong(src, Vector3f_x)); + UNSAFE.putFloat(null, destAddr+8, src.z); + } + + public static void put(Vector3d src, long destAddr) { + UNSAFE.putDouble(null, destAddr, src.x); + UNSAFE.putDouble(null, destAddr+8, src.y); + UNSAFE.putDouble(null, destAddr+16, src.z); + } + + public static void putf(Vector3d src, long destAddr) { + UNSAFE.putFloat(null, destAddr, (float) src.x); + UNSAFE.putFloat(null, destAddr+4, (float) src.y); + UNSAFE.putFloat(null, destAddr+8, (float) src.z); + } + + public static void put(Vector3i src, long destAddr) { + UNSAFE.putLong(null, destAddr, UNSAFE.getLong(src, Vector3i_x)); + UNSAFE.putInt(null, destAddr+8, src.z); + } + + public static void put(Vector2f src, long destAddr) { + UNSAFE.putLong(null, destAddr, UNSAFE.getLong(src, Vector2f_x)); + } + + public static void put(Vector2d src, long destAddr) { + UNSAFE.putDouble(null, destAddr, src.x); + UNSAFE.putDouble(null, destAddr+8, src.y); + } + + public static void put(Vector2i src, long destAddr) { + UNSAFE.putLong(null, destAddr, UNSAFE.getLong(src, Vector2i_x)); + } + + public static void get(Matrix4f m, long srcAddr) { + for (int i = 0; i < 8; i++) { + UNSAFE.putLong(m, Matrix4f_m00 + (i << 3), UNSAFE.getLong(srcAddr + (i << 3))); + } + } + + public static void getTransposed(Matrix4f m, long srcAddr) { + m._m00(UNSAFE.getFloat(srcAddr)) + ._m10(UNSAFE.getFloat(srcAddr + 4)) + ._m20(UNSAFE.getFloat(srcAddr + 8)) + ._m30(UNSAFE.getFloat(srcAddr + 12)) + ._m01(UNSAFE.getFloat(srcAddr + 16)) + ._m11(UNSAFE.getFloat(srcAddr + 20)) + ._m21(UNSAFE.getFloat(srcAddr + 24)) + ._m31(UNSAFE.getFloat(srcAddr + 28)) + ._m02(UNSAFE.getFloat(srcAddr + 32)) + ._m12(UNSAFE.getFloat(srcAddr + 36)) + ._m22(UNSAFE.getFloat(srcAddr + 40)) + ._m32(UNSAFE.getFloat(srcAddr + 44)) + ._m03(UNSAFE.getFloat(srcAddr + 48)) + ._m13(UNSAFE.getFloat(srcAddr + 52)) + ._m23(UNSAFE.getFloat(srcAddr + 56)) + ._m33(UNSAFE.getFloat(srcAddr + 60)); + } + + public static void get(Matrix4x3f m, long srcAddr) { + for (int i = 0; i < 6; i++) { + UNSAFE.putLong(m, Matrix4x3f_m00 + (i << 3), UNSAFE.getLong(srcAddr + (i << 3))); + } + } + + public static void get(Matrix4d m, long srcAddr) { + sun.misc.Unsafe u = UNSAFE; + m._m00(u.getDouble(null, srcAddr)) + ._m01(u.getDouble(null, srcAddr+8)) + ._m02(u.getDouble(null, srcAddr+16)) + ._m03(u.getDouble(null, srcAddr+24)) + ._m10(u.getDouble(null, srcAddr+32)) + ._m11(u.getDouble(null, srcAddr+40)) + ._m12(u.getDouble(null, srcAddr+48)) + ._m13(u.getDouble(null, srcAddr+56)) + ._m20(u.getDouble(null, srcAddr+64)) + ._m21(u.getDouble(null, srcAddr+72)) + ._m22(u.getDouble(null, srcAddr+80)) + ._m23(u.getDouble(null, srcAddr+88)) + ._m30(u.getDouble(null, srcAddr+96)) + ._m31(u.getDouble(null, srcAddr+104)) + ._m32(u.getDouble(null, srcAddr+112)) + ._m33(u.getDouble(null, srcAddr+120)); + } + + public static void get(Matrix4x3d m, long srcAddr) { + sun.misc.Unsafe u = UNSAFE; + m._m00(u.getDouble(null, srcAddr)) + ._m01(u.getDouble(null, srcAddr+8)) + ._m02(u.getDouble(null, srcAddr+16)) + ._m10(u.getDouble(null, srcAddr+24)) + ._m11(u.getDouble(null, srcAddr+32)) + ._m12(u.getDouble(null, srcAddr+40)) + ._m20(u.getDouble(null, srcAddr+48)) + ._m21(u.getDouble(null, srcAddr+56)) + ._m22(u.getDouble(null, srcAddr+64)) + ._m30(u.getDouble(null, srcAddr+72)) + ._m31(u.getDouble(null, srcAddr+80)) + ._m32(u.getDouble(null, srcAddr+88)); + } + + public static void getf(Matrix4d m, long srcAddr) { + sun.misc.Unsafe u = UNSAFE; + m._m00(u.getFloat(null, srcAddr)) + ._m01(u.getFloat(null, srcAddr+4)) + ._m02(u.getFloat(null, srcAddr+8)) + ._m03(u.getFloat(null, srcAddr+12)) + ._m10(u.getFloat(null, srcAddr+16)) + ._m11(u.getFloat(null, srcAddr+20)) + ._m12(u.getFloat(null, srcAddr+24)) + ._m13(u.getFloat(null, srcAddr+28)) + ._m20(u.getFloat(null, srcAddr+32)) + ._m21(u.getFloat(null, srcAddr+36)) + ._m22(u.getFloat(null, srcAddr+40)) + ._m23(u.getFloat(null, srcAddr+44)) + ._m30(u.getFloat(null, srcAddr+48)) + ._m31(u.getFloat(null, srcAddr+52)) + ._m32(u.getFloat(null, srcAddr+56)) + ._m33(u.getFloat(null, srcAddr+60)); + } + + public static void getf(Matrix4x3d m, long srcAddr) { + sun.misc.Unsafe u = UNSAFE; + m._m00(u.getFloat(null, srcAddr)) + ._m01(u.getFloat(null, srcAddr+4)) + ._m02(u.getFloat(null, srcAddr+8)) + ._m10(u.getFloat(null, srcAddr+12)) + ._m11(u.getFloat(null, srcAddr+16)) + ._m12(u.getFloat(null, srcAddr+20)) + ._m20(u.getFloat(null, srcAddr+24)) + ._m21(u.getFloat(null, srcAddr+28)) + ._m22(u.getFloat(null, srcAddr+32)) + ._m30(u.getFloat(null, srcAddr+36)) + ._m31(u.getFloat(null, srcAddr+40)) + ._m32(u.getFloat(null, srcAddr+44)); + } + + public static void get(Matrix3f m, long srcAddr) { + for (int i = 0; i < 4; i++) { + UNSAFE.putLong(m, Matrix3f_m00 + (i << 3), UNSAFE.getLong(null, srcAddr + (i << 3))); + } + m._m22(UNSAFE.getFloat(null, srcAddr+32)); + } + + public static void get(Matrix3d m, long srcAddr) { + sun.misc.Unsafe u = UNSAFE; + m._m00(u.getDouble(null, srcAddr)) + ._m01(u.getDouble(null, srcAddr+8)) + ._m02(u.getDouble(null, srcAddr+16)) + ._m10(u.getDouble(null, srcAddr+24)) + ._m11(u.getDouble(null, srcAddr+32)) + ._m12(u.getDouble(null, srcAddr+40)) + ._m20(u.getDouble(null, srcAddr+48)) + ._m21(u.getDouble(null, srcAddr+56)) + ._m22(u.getDouble(null, srcAddr+64)); + } + + public static void get(Matrix3x2f m, long srcAddr) { + for (int i = 0; i < 3; i++) { + UNSAFE.putLong(m, Matrix3x2f_m00 + (i << 3), UNSAFE.getLong(null, srcAddr + (i << 3))); + } + } + + public static void get(Matrix3x2d m, long srcAddr) { + sun.misc.Unsafe u = UNSAFE; + m._m00(u.getDouble(null, srcAddr)) + ._m01(u.getDouble(null, srcAddr+8)) + ._m10(u.getDouble(null, srcAddr+16)) + ._m11(u.getDouble(null, srcAddr+24)) + ._m20(u.getDouble(null, srcAddr+32)) + ._m21(u.getDouble(null, srcAddr+40)); + } + + public static void getf(Matrix3d m, long srcAddr) { + sun.misc.Unsafe u = UNSAFE; + m._m00(u.getFloat(null, srcAddr)) + ._m01(u.getFloat(null, srcAddr+4)) + ._m02(u.getFloat(null, srcAddr+8)) + ._m10(u.getFloat(null, srcAddr+12)) + ._m11(u.getFloat(null, srcAddr+16)) + ._m12(u.getFloat(null, srcAddr+20)) + ._m20(u.getFloat(null, srcAddr+24)) + ._m21(u.getFloat(null, srcAddr+28)) + ._m22(u.getFloat(null, srcAddr+32)); + } + + public static void get(Matrix2f m, long srcAddr) { + UNSAFE.putLong(m, Matrix2f_m00, UNSAFE.getLong(null, srcAddr)); + UNSAFE.putLong(m, Matrix2f_m00 + 8, UNSAFE.getLong(null, srcAddr + 8)); + } + + public static void get(Matrix2d m, long srcAddr) { + m._m00(UNSAFE.getDouble(null, srcAddr)) + ._m01(UNSAFE.getDouble(null, srcAddr+8)) + ._m10(UNSAFE.getDouble(null, srcAddr+16)) + ._m11(UNSAFE.getDouble(null, srcAddr+24)); + } + + public static void getf(Matrix2d m, long srcAddr) { + m._m00(UNSAFE.getFloat(null, srcAddr)) + ._m01(UNSAFE.getFloat(null, srcAddr+4)) + ._m10(UNSAFE.getFloat(null, srcAddr+8)) + ._m11(UNSAFE.getFloat(null, srcAddr+12)); + } + + public static void get(Vector4d dst, long srcAddr) { + dst.x = UNSAFE.getDouble(null, srcAddr); + dst.y = UNSAFE.getDouble(null, srcAddr+8); + dst.z = UNSAFE.getDouble(null, srcAddr+16); + dst.w = UNSAFE.getDouble(null, srcAddr+24); + } + + public static void get(Vector4f dst, long srcAddr) { + dst.x = UNSAFE.getFloat(null, srcAddr); + dst.y = UNSAFE.getFloat(null, srcAddr+4); + dst.z = UNSAFE.getFloat(null, srcAddr+8); + dst.w = UNSAFE.getFloat(null, srcAddr+12); + } + + public static void get(Vector4i dst, long srcAddr) { + dst.x = UNSAFE.getInt(null, srcAddr); + dst.y = UNSAFE.getInt(null, srcAddr+4); + dst.z = UNSAFE.getInt(null, srcAddr+8); + dst.w = UNSAFE.getInt(null, srcAddr+12); + } + + public static void get(Vector3f dst, long srcAddr) { + dst.x = UNSAFE.getFloat(null, srcAddr); + dst.y = UNSAFE.getFloat(null, srcAddr+4); + dst.z = UNSAFE.getFloat(null, srcAddr+8); + } + + public static void get(Vector3d dst, long srcAddr) { + dst.x = UNSAFE.getDouble(null, srcAddr); + dst.y = UNSAFE.getDouble(null, srcAddr+8); + dst.z = UNSAFE.getDouble(null, srcAddr+16); + } + + public static void get(Vector3i dst, long srcAddr) { + dst.x = UNSAFE.getInt(null, srcAddr); + dst.y = UNSAFE.getInt(null, srcAddr+4); + dst.z = UNSAFE.getInt(null, srcAddr+8); + } + + public static void get(Vector2f dst, long srcAddr) { + dst.x = UNSAFE.getFloat(null, srcAddr); + dst.y = UNSAFE.getFloat(null, srcAddr+4); + } + + public static void get(Vector2d dst, long srcAddr) { + dst.x = UNSAFE.getDouble(null, srcAddr); + dst.y = UNSAFE.getDouble(null, srcAddr+8); + } + + public static void get(Vector2i dst, long srcAddr) { + dst.x = UNSAFE.getInt(null, srcAddr); + dst.y = UNSAFE.getInt(null, srcAddr+4); + } + + public static void putMatrix3f(Quaternionf q, long addr) { + float dx = q.x + q.x; + float dy = q.y + q.y; + float dz = q.z + q.z; + float q00 = dx * q.x; + float q11 = dy * q.y; + float q22 = dz * q.z; + float q01 = dx * q.y; + float q02 = dx * q.z; + float q03 = dx * q.w; + float q12 = dy * q.z; + float q13 = dy * q.w; + float q23 = dz * q.w; + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, addr, 1.0f - q11 - q22); + u.putFloat(null, addr + 4, q01 + q23); + u.putFloat(null, addr + 8, q02 - q13); + u.putFloat(null, addr + 12, q01 - q23); + u.putFloat(null, addr + 16, 1.0f - q22 - q00); + u.putFloat(null, addr + 20, q12 + q03); + u.putFloat(null, addr + 24, q02 + q13); + u.putFloat(null, addr + 28, q12 - q03); + u.putFloat(null, addr + 32, 1.0f - q11 - q00); + } + + public static void putMatrix4f(Quaternionf q, long addr) { + float dx = q.x + q.x; + float dy = q.y + q.y; + float dz = q.z + q.z; + float q00 = dx * q.x; + float q11 = dy * q.y; + float q22 = dz * q.z; + float q01 = dx * q.y; + float q02 = dx * q.z; + float q03 = dx * q.w; + float q12 = dy * q.z; + float q13 = dy * q.w; + float q23 = dz * q.w; + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, addr, 1.0f - q11 - q22); + u.putFloat(null, addr + 4, q01 + q23); + u.putLong(null, addr + 8, Float.floatToRawIntBits(q02 - q13) & 0xFFFFFFFFL); + u.putFloat(null, addr + 16, q01 - q23); + u.putFloat(null, addr + 20, 1.0f - q22 - q00); + u.putLong(null, addr + 24, Float.floatToRawIntBits(q12 + q03) & 0xFFFFFFFFL); + u.putFloat(null, addr + 32, q02 + q13); + u.putFloat(null, addr + 36, q12 - q03); + u.putLong(null, addr + 40, Float.floatToRawIntBits(1.0f - q11 - q00) & 0xFFFFFFFFL); + u.putLong(null, addr + 48, 0L); + u.putLong(null, addr + 56, 0x3F80000000000000L); + } + + public static void putMatrix4x3f(Quaternionf q, long addr) { + float dx = q.x + q.x; + float dy = q.y + q.y; + float dz = q.z + q.z; + float q00 = dx * q.x; + float q11 = dy * q.y; + float q22 = dz * q.z; + float q01 = dx * q.y; + float q02 = dx * q.z; + float q03 = dx * q.w; + float q12 = dy * q.z; + float q13 = dy * q.w; + float q23 = dz * q.w; + sun.misc.Unsafe u = UNSAFE; + u.putFloat(null, addr, 1.0f - q11 - q22); + u.putFloat(null, addr + 4, q01 + q23); + u.putFloat(null, addr + 8, q02 - q13); + u.putFloat(null, addr + 12, q01 - q23); + u.putFloat(null, addr + 16, 1.0f - q22 - q00); + u.putFloat(null, addr + 20, q12 + q03); + u.putFloat(null, addr + 24, q02 + q13); + u.putFloat(null, addr + 28, q12 - q03); + u.putFloat(null, addr + 32, 1.0f - q11 - q00); + u.putLong(null, addr + 36, 0L); + u.putFloat(null, addr + 44, 0.0f); + } + + private static void throwNoDirectBufferException() { + throw new IllegalArgumentException("Must use a direct buffer"); + } + + public void putMatrix3f(Quaternionf q, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9 << 2); + putMatrix3f(q, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putMatrix3f(Quaternionf q, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9); + putMatrix3f(q, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + private static void checkPut(int offset, boolean direct, int capacity, int i) { + if (!direct) + throwNoDirectBufferException(); + if (capacity - offset < i) + throw new BufferOverflowException(); + } + + public void putMatrix4f(Quaternionf q, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 2); + putMatrix4f(q, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putMatrix4f(Quaternionf q, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + putMatrix4f(q, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putMatrix4x3f(Quaternionf q, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + putMatrix4x3f(q, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putMatrix4x3f(Quaternionf q, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + putMatrix4x3f(q, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Matrix4f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Matrix4f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 2); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put4x3(Matrix4f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + put4x3(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put4x3(Matrix4f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + put4x3(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put3x4(Matrix4f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + put3x4(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put3x4(Matrix4f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + put3x4(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix4x3f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Matrix4x3f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put4x4(Matrix4x3f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + put4x4(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put4x4(Matrix4x3f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 2); + put4x4(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put3x4(Matrix4x3f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + put3x4(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put3x4(Matrix4x3f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + put3x4(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put4x4(Matrix4x3d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + put4x4(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put4x4(Matrix4x3d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 3); + put4x4(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put4x4(Matrix3x2f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + put4x4(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put4x4(Matrix3x2f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 2); + put4x4(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put4x4(Matrix3x2d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + put4x4(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put4x4(Matrix3x2d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 3); + put4x4(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put3x3(Matrix3x2f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9); + put3x3(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put3x3(Matrix3x2f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9 << 2); + put3x3(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put3x3(Matrix3x2d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9); + put3x3(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put3x3(Matrix3x2d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9 << 3); + put3x3(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putTransposed(Matrix4f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putTransposed(Matrix4f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 2); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put4x3Transposed(Matrix4f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + put4x3Transposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put4x3Transposed(Matrix4f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + put4x3Transposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putTransposed(Matrix4x3f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putTransposed(Matrix4x3f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putTransposed(Matrix3f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putTransposed(Matrix3f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9 << 2); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putTransposed(Matrix2f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putTransposed(Matrix2f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 2); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix4d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put(Matrix4d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 3); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix4x3d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put(Matrix4x3d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 3); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putf(Matrix4d m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + putf(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putf(Matrix4d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 2); + putf(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putf(Matrix4x3d m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + putf(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putf(Matrix4x3d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + putf(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putTransposed(Matrix4d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void putTransposed(Matrix4d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 3); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put4x3Transposed(Matrix4d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + put4x3Transposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put4x3Transposed(Matrix4d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 3); + put4x3Transposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putTransposed(Matrix4x3d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void putTransposed(Matrix4x3d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 3); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putTransposed(Matrix2d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void putTransposed(Matrix2d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 3); + putTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putfTransposed(Matrix4d m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16); + putfTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putfTransposed(Matrix4d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 16 << 2); + putfTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putfTransposed(Matrix4x3d m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + putfTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putfTransposed(Matrix4x3d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + putfTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putfTransposed(Matrix2d m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + putfTransposed(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putfTransposed(Matrix2d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 2); + putfTransposed(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix3f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Matrix3f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9 << 2); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put3x4(Matrix3f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12); + put3x4(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put3x4(Matrix3f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 12 << 2); + put3x4(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix3d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put(Matrix3d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9 << 3); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix3x2f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 6); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Matrix3x2f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 6 << 2); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix3x2d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 6); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put(Matrix3x2d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 6 << 3); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putf(Matrix3d m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9); + putf(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putf(Matrix3d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 9 << 2); + putf(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix2f m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Matrix2f m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 2); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Matrix2d m, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + put(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put(Matrix2d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 2 << 3); + put(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putf(Matrix2d m, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + putf(m, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void putf(Matrix2d m, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 2); + putf(m, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector4d src, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put(Vector4d src, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + putf(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Vector4d src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 3); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putf(Vector4d src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 2); + putf(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector4f src, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Vector4f src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector4i src, int offset, IntBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Vector4i src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 4 << 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector3f src, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 3); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Vector3f src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 3 << 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector3d src, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 3); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put(Vector3d src, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 3); + putf(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Vector3d src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 3 << 3); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void putf(Vector3d src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 3 << 2); + putf(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector3i src, int offset, IntBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 3); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Vector3i src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 3 << 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector2f src, int offset, FloatBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Vector2f src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 2 << 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector2d src, int offset, DoubleBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 3)); + } + + public void put(Vector2d src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 2 << 3); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void put(Vector2i src, int offset, IntBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + (offset << 2)); + } + + public void put(Vector2i src, int offset, ByteBuffer dest) { + if (Options.DEBUG) checkPut(offset, dest.isDirect(), dest.capacity(), 2 << 2); + put(src, UNSAFE.getLong(dest, ADDRESS) + offset); + } + + public void get(Matrix4f m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 16); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Matrix4f m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 16 << 2); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public float get(Matrix4f m, int column, int row) { + return UNSAFE.getFloat(m, Matrix4f_m00 + (column << 4) + (row << 2)); + } + + public Matrix4f set(Matrix4f m, int column, int row, float value) { + UNSAFE.putFloat(m, Matrix4f_m00 + (column << 4) + (row << 2), value); + return m; + } + + public double get(Matrix4d m, int column, int row) { + return UNSAFE.getDouble(m, Matrix4d_m00 + (column << 5) + (row << 3)); + } + + public Matrix4d set(Matrix4d m, int column, int row, double value) { + UNSAFE.putDouble(m, Matrix4d_m00 + (column << 5) + (row << 3), value); + return m; + } + + public float get(Matrix3f m, int column, int row) { + return UNSAFE.getFloat(m, Matrix3f_m00 + (column * (3<<2)) + (row << 2)); + } + + public Matrix3f set(Matrix3f m, int column, int row, float value) { + UNSAFE.putFloat(m, Matrix3f_m00 + (column * (3<<2)) + (row << 2), value); + return m; + } + + public double get(Matrix3d m, int column, int row) { + return UNSAFE.getDouble(m, Matrix3d_m00 + (column * (3<<3)) + (row << 3)); + } + + public Matrix3d set(Matrix3d m, int column, int row, double value) { + UNSAFE.putDouble(m, Matrix3d_m00 + (column * (3<<3)) + (row << 3), value); + return m; + } + + public void get(Matrix4x3f m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 12); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Matrix4x3f m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 12 << 2); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Matrix4d m, int offset, DoubleBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 16); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 3)); + } + + public void get(Matrix4d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 16 << 3); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Matrix4x3d m, int offset, DoubleBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 12); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 3)); + } + + public void get(Matrix4x3d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 12 << 3); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void getf(Matrix4d m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 16); + getf(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void getf(Matrix4d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 16 << 2); + getf(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void getf(Matrix4x3d m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 12); + getf(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + private static void checkGet(int offset, boolean direct, int capacity, int i) { + if (!direct) + throwNoDirectBufferException(); + if (capacity - offset < i) + throw new BufferUnderflowException(); + } + + public void getf(Matrix4x3d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 12 << 2); + getf(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Matrix3f m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 9); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Matrix3f m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 9 << 2); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Matrix3d m, int offset, DoubleBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 9); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 3)); + } + + public void get(Matrix3d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 9 << 3); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Matrix3x2f m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 6); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Matrix3x2f m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 6 << 2); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Matrix3x2d m, int offset, DoubleBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 6); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 3)); + } + + public void get(Matrix3x2d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 6 << 3); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void getf(Matrix3d m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 9); + getf(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void getf(Matrix3d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 9 << 2); + getf(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Matrix2f m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Matrix2f m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4 << 2); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Matrix2d m, int offset, DoubleBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4); + get(m, UNSAFE.getLong(src, ADDRESS) + (offset << 3)); + } + + public void get(Matrix2d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4 << 3); + get(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void getf(Matrix2d m, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4); + getf(m, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void getf(Matrix2d m, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4 << 2); + getf(m, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector4d dst, int offset, DoubleBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 3)); + } + + public void get(Vector4d dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4 << 3); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector4f dst, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Vector4f dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4 << 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector4i dst, int offset, IntBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Vector4i dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 4 << 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector3f dst, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 3); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Vector3f dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 3 << 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector3d dst, int offset, DoubleBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 3); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 3)); + } + + public void get(Vector3d dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 3 << 3); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector3i dst, int offset, IntBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 3); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Vector3i dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 3 << 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector2f dst, int offset, FloatBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Vector2f dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 2 << 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector2d dst, int offset, DoubleBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 3)); + } + + public void get(Vector2d dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 2 << 3); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + + public void get(Vector2i dst, int offset, IntBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + (offset << 2)); + } + + public void get(Vector2i dst, int offset, ByteBuffer src) { + if (Options.DEBUG) checkGet(offset, src.isDirect(), src.capacity(), 2 << 2); + get(dst, UNSAFE.getLong(src, ADDRESS) + offset); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Options.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Options.java new file mode 100644 index 000000000..efee2e259 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Options.java @@ -0,0 +1,116 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Arrays; +import java.util.Locale; + +/** + * Utility class for reading system properties. + * + * @author Kai Burjack + */ +public final class Options { + + /** + * Whether certain debugging checks should be made, such as that only direct NIO Buffers are used when Unsafe is active, + * and a proxy should be created on calls to readOnlyView(). + */ + public static final boolean DEBUG = hasOption(System.getProperty("joml.debug", "false")); + + /** + * Whether not to use sun.misc.Unsafe when copying memory with MemUtil. + */ + public static final boolean NO_UNSAFE = hasOption(System.getProperty("joml.nounsafe", "false")); + /** + * Whether to force the use of sun.misc.Unsafe when copying memory with MemUtil. + */ + public static final boolean FORCE_UNSAFE = hasOption(System.getProperty("joml.forceUnsafe", "false")); + + /** + * Whether fast approximations of some java.lang.Math operations should be used. + */ + public static final boolean FASTMATH = hasOption(System.getProperty("joml.fastmath", "false")); + + /** + * When {@link #FASTMATH} is true, whether to use a lookup table for sin/cos. + */ + public static final boolean SIN_LOOKUP = hasOption(System.getProperty("joml.sinLookup", "false")); + + /** + * When {@link #SIN_LOOKUP} is true, this determines the table size. + */ + public static final int SIN_LOOKUP_BITS = Integer.parseInt(System.getProperty("joml.sinLookup.bits", "14")); + + /** + * Whether to use a {@link NumberFormat} producing scientific notation output when formatting matrix, + * vector and quaternion components to strings. + */ + public static final boolean useNumberFormat = hasOption(System.getProperty("joml.format", "true")); + + /** + * Whether to try using java.lang.Math.fma() in most matrix/vector/quaternion operations if it is available. + * If the CPU does not support it, it will be a lot slower than `a*b+c` and potentially generate a lot of memory allocations + * for the emulation with `java.util.BigDecimal`, though. + */ + public static final boolean USE_MATH_FMA = hasOption(System.getProperty("joml.useMathFma", "false")); + + /** + * When {@link #useNumberFormat} is true then this determines the number of decimal digits + * produced in the formatted numbers. + */ + public static final int numberFormatDecimals = Integer.parseInt(System.getProperty("joml.format.decimals", "3")); + + /** + * The {@link NumberFormat} used to format all numbers throughout all JOML classes. + */ + public static final NumberFormat NUMBER_FORMAT = decimalFormat(); + + private Options() { + } + + private static NumberFormat decimalFormat() { + NumberFormat df; + if (useNumberFormat) { + char[] prec = new char[numberFormatDecimals]; + Arrays.fill(prec, '0'); + df = new DecimalFormat(" 0." + new String(prec) + "E0;-"); + } else { + df = NumberFormat.getNumberInstance(Locale.ENGLISH); + df.setGroupingUsed(false); + } + return df; + } + + private static boolean hasOption(String v) { + if (v == null) + return false; + if (v.trim().length() == 0) + return true; + return Boolean.valueOf(v).booleanValue(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/PolygonsIntersection.java b/src/main/java/com/jozufozu/flywheel/repack/joml/PolygonsIntersection.java new file mode 100644 index 000000000..c44f23e35 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/PolygonsIntersection.java @@ -0,0 +1,328 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Class for polygon/point intersection tests when testing many points against one or many static concave or convex, simple polygons. + *

+ * This is an implementation of the algorithm described in http://alienryderflex.com and augmented with using a + * custom interval tree to avoid testing all polygon edges against a point, but only those that intersect the imaginary ray along the same y co-ordinate of the + * search point. This algorithm additionally also supports multiple polygons. + *

+ * This class is thread-safe and can be used in a multithreaded environment when testing many points against the same polygon concurrently. + *

+ * Reference: http://alienryderflex.com + * + * @author Kai Burjack + */ +public class PolygonsIntersection { + + static class ByStartComparator implements Comparator { + public int compare(Object o1, Object o2) { + Interval i1 = (Interval) o1; + Interval i2 = (Interval) o2; + return Float.compare(i1.start, i2.start); + } + } + + static class ByEndComparator implements Comparator { + public int compare(Object o1, Object o2) { + Interval i1 = (Interval) o1; + Interval i2 = (Interval) o2; + return Float.compare(i2.end, i1.end); + } + } + + static class Interval { + float start, end; + int i, j, polyIndex; + } + + static class IntervalTreeNode { + float center; + float childrenMinMax; + IntervalTreeNode left; + IntervalTreeNode right; + List/* */ byBeginning; + List/* */ byEnding; + + static boolean computeEvenOdd(float[] verticesXY, Interval ival, float x, float y, boolean evenOdd, BitSet inPolys) { + boolean newEvenOdd = evenOdd; + int i = ival.i; + int j = ival.j; + float yi = verticesXY[2 * i + 1]; + float yj = verticesXY[2 * j + 1]; + float xi = verticesXY[2 * i + 0]; + float xj = verticesXY[2 * j + 0]; + if ((yi < y && yj >= y || yj < y && yi >= y) && (xi <= x || xj <= x)) { + float xDist = xi + (y - yi) / (yj - yi) * (xj - xi) - x; + newEvenOdd ^= xDist < 0.0f; + if (newEvenOdd != evenOdd && inPolys != null) { + inPolys.flip(ival.polyIndex); + } + } + return newEvenOdd; + } + + boolean traverse(float[] verticesXY, float x, float y, boolean evenOdd, BitSet inPolys) { + boolean newEvenOdd = evenOdd; + if (y == center && byBeginning != null) { + int size = byBeginning.size(); + for (int b = 0; b < size; b++) { + Interval ival = (Interval) byBeginning.get(b); + newEvenOdd = computeEvenOdd(verticesXY, ival, x, y, newEvenOdd, inPolys); + } + } else if (y < center) { + if (left != null && left.childrenMinMax >= y) + newEvenOdd = left.traverse(verticesXY, x, y, newEvenOdd, inPolys); + if (byBeginning != null) { + int size = byBeginning.size(); + for (int b = 0; b < size; b++) { + Interval ival = (Interval) byBeginning.get(b); + if (ival.start > y) + break; + newEvenOdd = computeEvenOdd(verticesXY, ival, x, y, newEvenOdd, inPolys); + } + } + } else if (y > center) { + if (right != null && right.childrenMinMax <= y) + newEvenOdd = right.traverse(verticesXY, x, y, newEvenOdd, inPolys); + if (byEnding != null) { + int size = byEnding.size(); + for (int b = 0; b < size; b++) { + Interval ival = (Interval) byEnding.get(b); + if (ival.end < y) + break; + newEvenOdd = computeEvenOdd(verticesXY, ival, x, y, newEvenOdd, inPolys); + } + } + } + return newEvenOdd; + } + } + + private static final ByStartComparator byStartComparator = new ByStartComparator(); + private static final ByEndComparator byEndComparator = new ByEndComparator(); + + protected final float[] verticesXY; + private float minX, minY, maxX, maxY; + private float centerX, centerY, radiusSquared; + private IntervalTreeNode tree; + + /** + * Create a new {@link PolygonsIntersection} object with the given polygon vertices. + *

+ * The verticesXY array contains the x and y coordinates of all vertices. This array will not be copied so its content must remain constant for + * as long as the PolygonPointIntersection is used with it. + * + * @param verticesXY + * contains the x and y coordinates of all vertices + * @param polygons + * defines the start vertices of a new polygon. The first vertex of the first polygon is always the + * vertex with index 0. In order to define a hole simply define a polygon that is completely inside another polygon + * @param count + * the number of vertices to use from the verticesXY array, staring with index 0 + */ + public PolygonsIntersection(float[] verticesXY, int[] polygons, int count) { + this.verticesXY = verticesXY; + // Do all the allocations and initializations during this constructor + preprocess(count, polygons); + } + + private IntervalTreeNode buildNode(List intervals, float center) { + List left = null; + List right = null; + List byStart = null; + List byEnd = null; + float leftMin = 1E38f, leftMax = -1E38f, rightMin = 1E38f, rightMax = -1E38f; + float thisMin = 1E38f, thisMax = -1E38f; + for (int i = 0; i < intervals.size(); i++) { + Interval ival = (Interval) intervals.get(i); + if (ival.start < center && ival.end < center) { + if (left == null) + left = new ArrayList(); + left.add(ival); + leftMin = leftMin < ival.start ? leftMin : ival.start; + leftMax = leftMax > ival.end ? leftMax : ival.end; + } else if (ival.start > center && ival.end > center) { + if (right == null) + right = new ArrayList(); + right.add(ival); + rightMin = rightMin < ival.start ? rightMin : ival.start; + rightMax = rightMax > ival.end ? rightMax : ival.end; + } else { + if (byStart == null || byEnd == null) { + byStart = new ArrayList(); + byEnd = new ArrayList(); + } + thisMin = ival.start < thisMin ? ival.start : thisMin; + thisMax = ival.end > thisMax ? ival.end : thisMax; + byStart.add(ival); + byEnd.add(ival); + } + } + if (byStart != null) { + Collections.sort(byStart, byStartComparator); + Collections.sort(byEnd, byEndComparator); + } + IntervalTreeNode tree = new IntervalTreeNode(); + tree.byBeginning = byStart; + tree.byEnding = byEnd; + tree.center = center; + if (left != null) { + tree.left = buildNode(left, (leftMin + leftMax) / 2.0f); + tree.left.childrenMinMax = leftMax; + } + if (right != null) { + tree.right = buildNode(right, (rightMin + rightMax) / 2.0f); + tree.right.childrenMinMax = rightMin; + } + return tree; + } + + private void preprocess(int count, int[] polygons) { + int i, j = 0; + minX = minY = 1E38f; + maxX = maxY = -1E38f; + List intervals = new ArrayList(count); + int first = 0; + int currPoly = 0; + for (i = 1; i < count; i++) { + if (polygons != null && polygons.length > currPoly && polygons[currPoly] == i) { + /* New polygon starts. End the current. */ + float prevy = verticesXY[2 * (i - 1) + 1]; + float firsty = verticesXY[2 * first + 1]; + Interval ival = new Interval(); + ival.start = prevy < firsty ? prevy : firsty; + ival.end = firsty > prevy ? firsty : prevy; + ival.i = i - 1; + ival.j = first; + ival.polyIndex = currPoly; + intervals.add(ival); + first = i; + currPoly++; + i++; + j = i - 1; + } + float yi = verticesXY[2 * i + 1]; + float xi = verticesXY[2 * i + 0]; + float yj = verticesXY[2 * j + 1]; + minX = xi < minX ? xi : minX; + minY = yi < minY ? yi : minY; + maxX = xi > maxX ? xi : maxX; + maxY = yi > maxY ? yi : maxY; + Interval ival = new Interval(); + ival.start = yi < yj ? yi : yj; + ival.end = yj > yi ? yj : yi; + ival.i = i; + ival.j = j; + ival.polyIndex = currPoly; + intervals.add(ival); + j = i; + } + // Close current polygon + float yi = verticesXY[2 * (i - 1) + 1]; + float xi = verticesXY[2 * (i - 1) + 0]; + float yj = verticesXY[2 * first + 1]; + minX = xi < minX ? xi : minX; + minY = yi < minY ? yi : minY; + maxX = xi > maxX ? xi : maxX; + maxY = yi > maxY ? yi : maxY; + Interval ival = new Interval(); + ival.start = yi < yj ? yi : yj; + ival.end = yj > yi ? yj : yi; + ival.i = i - 1; + ival.j = first; + ival.polyIndex = currPoly; + intervals.add(ival); + // compute bounding sphere and rectangle + centerX = (maxX + minX) * 0.5f; + centerY = (maxY + minY) * 0.5f; + float dx = maxX - centerX; + float dy = maxY - centerY; + radiusSquared = dx * dx + dy * dy; + // build interval tree + tree = buildNode(intervals, centerY); + } + + /** + * Test whether the given point (x, y) lies inside any polygon stored in this {@link PolygonsIntersection} object. + *

+ * This method is thread-safe and can be used to test many points concurrently. + *

+ * In order to obtain the index of the polygon the point is inside of, use {@link #testPoint(float, float, BitSet)} + * + * @see #testPoint(float, float, BitSet) + * + * @param x + * the x coordinate of the point to test + * @param y + * the y coordinate of the point to test + * @return true iff the point lies inside any polygon; false otherwise + */ + public boolean testPoint(float x, float y) { + return testPoint(x, y, null); + } + + /** + * Test whether the given point (x, y) lies inside any polygon stored in this {@link PolygonsIntersection} object. + *

+ * This method is thread-safe and can be used to test many points concurrently. + * + * @param x + * the x coordinate of the point to test + * @param y + * the y coordinate of the point to test + * @param inPolys + * if not null then the i-th bit is set if the given point is inside the i-th polygon + * @return true iff the point lies inside the polygon and not inside a hole; false otherwise + */ + public boolean testPoint(float x, float y, BitSet inPolys) { + // check bounding sphere first + float dx = (x - centerX); + float dy = (y - centerY); + if (inPolys != null) + inPolys.clear(); + if (dx * dx + dy * dy > radiusSquared) + return false; + // check bounding box next + if (maxX < x || maxY < y || minX > x || minY > y) + return false; + // ask interval tree for all polygon edges intersecting 'y' and perform + // the even/odd/crosscutting/raycast algorithm on them and also return + // the polygon index of the polygon the point is in by setting the appropriate + // bit in the given BitSet. + boolean res = tree.traverse(verticesXY, x, y, false, inPolys); + return res; + } + +} + diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Quaterniond.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Quaterniond.java new file mode 100644 index 000000000..7b4a33e29 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Quaterniond.java @@ -0,0 +1,2985 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Quaternion of 4 double-precision floats which can represent rotation and uniform scaling. + * + * @author Richard Greenlees + * @author Kai Burjack + */ +public class Quaterniond implements Externalizable, Cloneable, Quaterniondc { + + private static final long serialVersionUID = 1L; + + /** + * The first component of the vector part. + */ + public double x; + /** + * The second component of the vector part. + */ + public double y; + /** + * The third component of the vector part. + */ + public double z; + /** + * The real/scalar part of the quaternion. + */ + public double w; + + /** + * Create a new {@link Quaterniond} and initialize it with (x=0, y=0, z=0, w=1), + * where (x, y, z) is the vector part of the quaternion and w is the real/scalar part. + */ + public Quaterniond() { + this.w = 1.0; + } + + /** + * Create a new {@link Quaterniond} and initialize its components to the given values. + * + * @param x + * the first component of the imaginary part + * @param y + * the second component of the imaginary part + * @param z + * the third component of the imaginary part + * @param w + * the real part + */ + public Quaterniond(double x, double y, double z, double w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Quaterniond} and initialize its components to the same values as the given {@link Quaterniondc}. + * + * @param source + * the {@link Quaterniondc} to take the component values from + */ + public Quaterniond(Quaterniondc source) { + x = source.x(); + y = source.y(); + z = source.z(); + w = source.w(); + } + + /** + * Create a new {@link Quaterniond} and initialize its components to the same values as the given {@link Quaternionfc}. + * + * @param source + * the {@link Quaternionfc} to take the component values from + */ + public Quaterniond(Quaternionfc source) { + x = source.x(); + y = source.y(); + z = source.z(); + w = source.w(); + } + + /** + * Create a new {@link Quaterniond} and initialize it to represent the same rotation as the given {@link AxisAngle4f}. + * + * @param axisAngle + * the axis-angle to initialize this quaternion with + */ + public Quaterniond(AxisAngle4f axisAngle) { + double s = Math.sin(axisAngle.angle * 0.5); + x = axisAngle.x * s; + y = axisAngle.y * s; + z = axisAngle.z * s; + w = Math.cosFromSin(s, axisAngle.angle * 0.5); + } + + /** + * Create a new {@link Quaterniond} and initialize it to represent the same rotation as the given {@link AxisAngle4d}. + * + * @param axisAngle + * the axis-angle to initialize this quaternion with + */ + public Quaterniond(AxisAngle4d axisAngle) { + double s = Math.sin(axisAngle.angle * 0.5); + x = axisAngle.x * s; + y = axisAngle.y * s; + z = axisAngle.z * s; + w = Math.cosFromSin(s, axisAngle.angle * 0.5); + } + + /** + * @return the first component of the vector part + */ + public double x() { + return this.x; + } + + /** + * @return the second component of the vector part + */ + public double y() { + return this.y; + } + + /** + * @return the third component of the vector part + */ + public double z() { + return this.z; + } + + /** + * @return the real/scalar part of the quaternion + */ + public double w() { + return this.w; + } + + /** + * Normalize this quaternion. + * + * @return this + */ + public Quaterniond normalize() { + double invNorm = Math.invsqrt(lengthSquared()); + x *= invNorm; + y *= invNorm; + z *= invNorm; + w *= invNorm; + return this; + } + + public Quaterniond normalize(Quaterniond dest) { + double invNorm = Math.invsqrt(lengthSquared()); + dest.x = x * invNorm; + dest.y = y * invNorm; + dest.z = z * invNorm; + dest.w = w * invNorm; + return dest; + } + + /** + * Add the quaternion (x, y, z, w) to this quaternion. + * + * @param x + * the x component of the vector part + * @param y + * the y component of the vector part + * @param z + * the z component of the vector part + * @param w + * the real/scalar component + * @return this + */ + public Quaterniond add(double x, double y, double z, double w) { + return add(x, y, z, w, this); + } + + public Quaterniond add(double x, double y, double z, double w, Quaterniond dest) { + dest.x = this.x + x; + dest.y = this.y + y; + dest.z = this.z + z; + dest.w = this.w + w; + return dest; + } + + /** + * Add q2 to this quaternion. + * + * @param q2 + * the quaternion to add to this + * @return this + */ + public Quaterniond add(Quaterniondc q2) { + x += q2.x(); + y += q2.y(); + z += q2.z(); + w += q2.w(); + return this; + } + + public Quaterniond add(Quaterniondc q2, Quaterniond dest) { + dest.x = x + q2.x(); + dest.y = y + q2.y(); + dest.z = z + q2.z(); + dest.w = w + q2.w(); + return dest; + } + + public double dot(Quaterniondc otherQuat) { + return this.x * otherQuat.x() + this.y * otherQuat.y() + this.z * otherQuat.z() + this.w * otherQuat.w(); + } + + public double angle() { + return 2.0 * Math.safeAcos(w); + } + + public Matrix3d get(Matrix3d dest) { + return dest.set(this); + } + + public Matrix3f get(Matrix3f dest) { + return dest.set(this); + } + + public Matrix4d get(Matrix4d dest) { + return dest.set(this); + } + + public Matrix4f get(Matrix4f dest) { + return dest.set(this); + } + + public AxisAngle4f get(AxisAngle4f dest) { + double x = this.x; + double y = this.y; + double z = this.z; + double w = this.w; + if (w > 1.0) { + double invNorm = Math.invsqrt(lengthSquared()); + x *= invNorm; + y *= invNorm; + z *= invNorm; + w *= invNorm; + } + dest.angle = (float) (2.0 * Math.acos(w)); + double s = Math.sqrt(1.0 - w * w); + if (s < 0.001) { + dest.x = (float) x; + dest.y = (float) y; + dest.z = (float) z; + } else { + s = 1.0 / s; + dest.x = (float) (x * s); + dest.y = (float) (y * s); + dest.z = (float) (z * s); + } + return dest; + } + + public AxisAngle4d get(AxisAngle4d dest) { + double x = this.x; + double y = this.y; + double z = this.z; + double w = this.w; + if (w > 1.0) { + double invNorm = Math.invsqrt(lengthSquared()); + x *= invNorm; + y *= invNorm; + z *= invNorm; + w *= invNorm; + } + dest.angle = 2.0 * Math.acos(w); + double s = Math.sqrt(1.0 - w * w); + if (s < 0.001) { + dest.x = x; + dest.y = y; + dest.z = z; + } else { + s = 1.0 / s; + dest.x = x * s; + dest.y = y * s; + dest.z = z * s; + } + return dest; + } + + /** + * Set the given {@link Quaterniond} to the values of this. + * + * @see #set(Quaterniondc) + * + * @param dest + * the {@link Quaterniond} to set + * @return the passed in destination + */ + public Quaterniond get(Quaterniond dest) { + return dest.set(this); + } + + /** + * Set the given {@link Quaternionf} to the values of this. + * + * @see #set(Quaterniondc) + * + * @param dest + * the {@link Quaternionf} to set + * @return the passed in destination + */ + public Quaternionf get(Quaternionf dest) { + return dest.set(this); + } + + /** + * Set this quaternion to the new values. + * + * @param x + * the new value of x + * @param y + * the new value of y + * @param z + * the new value of z + * @param w + * the new value of w + * @return this + */ + public Quaterniond set(double x, double y, double z, double w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + /** + * Set this quaternion to be a copy of q. + * + * @param q + * the {@link Quaterniondc} to copy + * @return this + */ + public Quaterniond set(Quaterniondc q) { + x = q.x(); + y = q.y(); + z = q.z(); + w = q.w(); + return this; + } + + /** + * Set this quaternion to be a copy of q. + * + * @param q + * the {@link Quaternionfc} to copy + * @return this + */ + public Quaterniond set(Quaternionfc q) { + x = q.x(); + y = q.y(); + z = q.z(); + w = q.w(); + return this; + } + + /** + * Set this {@link Quaterniond} to be equivalent to the given + * {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + * @return this + */ + public Quaterniond set(AxisAngle4f axisAngle) { + return setAngleAxis(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this {@link Quaterniond} to be equivalent to the given + * {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + * @return this + */ + public Quaterniond set(AxisAngle4d axisAngle) { + return setAngleAxis(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this quaternion to a rotation equivalent to the supplied axis and + * angle (in radians). + *

+ * This method assumes that the given rotation axis (x, y, z) is already normalized + * + * @param angle + * the angle in radians + * @param x + * the x-component of the normalized rotation axis + * @param y + * the y-component of the normalized rotation axis + * @param z + * the z-component of the normalized rotation axis + * @return this + */ + public Quaterniond setAngleAxis(double angle, double x, double y, double z) { + double s = Math.sin(angle * 0.5); + this.x = x * s; + this.y = y * s; + this.z = z * s; + this.w = Math.cosFromSin(s, angle * 0.5); + return this; + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in radians). + * + * @param angle + * the angle in radians + * @param axis + * the rotation axis + * @return this + */ + public Quaterniond setAngleAxis(double angle, Vector3dc axis) { + return setAngleAxis(angle, axis.x(), axis.y(), axis.z()); + } + + private void setFromUnnormalized(double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22) { + double nm00 = m00, nm01 = m01, nm02 = m02; + double nm10 = m10, nm11 = m11, nm12 = m12; + double nm20 = m20, nm21 = m21, nm22 = m22; + double lenX = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + double lenY = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + double lenZ = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + setFromNormalized(nm00, nm01, nm02, nm10, nm11, nm12, nm20, nm21, nm22); + } + + private void setFromNormalized(double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22) { + double t; + double tr = m00 + m11 + m22; + if (tr >= 0.0) { + t = Math.sqrt(tr + 1.0); + w = t * 0.5; + t = 0.5 / t; + x = (m12 - m21) * t; + y = (m20 - m02) * t; + z = (m01 - m10) * t; + } else { + if (m00 >= m11 && m00 >= m22) { + t = Math.sqrt(m00 - (m11 + m22) + 1.0); + x = t * 0.5; + t = 0.5 / t; + y = (m10 + m01) * t; + z = (m02 + m20) * t; + w = (m12 - m21) * t; + } else if (m11 > m22) { + t = Math.sqrt(m11 - (m22 + m00) + 1.0); + y = t * 0.5; + t = 0.5 / t; + z = (m21 + m12) * t; + x = (m10 + m01) * t; + w = (m20 - m02) * t; + } else { + t = Math.sqrt(m22 - (m00 + m11) + 1.0); + z = t * 0.5; + t = 0.5 / t; + x = (m02 + m20) * t; + y = (m21 + m12) * t; + w = (m01 - m10) * t; + } + } + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromUnnormalized(Matrix4fc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromUnnormalized(Matrix4x3fc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromUnnormalized(Matrix4x3dc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromNormalized(Matrix4fc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromNormalized(Matrix4x3fc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromNormalized(Matrix4x3dc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromUnnormalized(Matrix4dc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromNormalized(Matrix4dc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromUnnormalized(Matrix3fc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromNormalized(Matrix3fc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromUnnormalized(Matrix3dc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaterniond setFromNormalized(Matrix3dc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in radians). + * + * @param axis + * the rotation axis + * @param angle + * the angle in radians + * @return this + */ + public Quaterniond fromAxisAngleRad(Vector3dc axis, double angle) { + return fromAxisAngleRad(axis.x(), axis.y(), axis.z(), angle); + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in radians). + * + * @param axisX + * the x component of the rotation axis + * @param axisY + * the y component of the rotation axis + * @param axisZ + * the z component of the rotation axis + * @param angle + * the angle in radians + * @return this + */ + public Quaterniond fromAxisAngleRad(double axisX, double axisY, double axisZ, double angle) { + double hangle = angle / 2.0; + double sinAngle = Math.sin(hangle); + double vLength = Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ); + x = axisX / vLength * sinAngle; + y = axisY / vLength * sinAngle; + z = axisZ / vLength * sinAngle; + w = Math.cosFromSin(sinAngle, hangle); + return this; + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in degrees). + * + * @param axis + * the rotation axis + * @param angle + * the angle in degrees + * @return this + */ + public Quaterniond fromAxisAngleDeg(Vector3dc axis, double angle) { + return fromAxisAngleRad(axis.x(), axis.y(), axis.z(), Math.toRadians(angle)); + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in degrees). + * + * @param axisX + * the x component of the rotation axis + * @param axisY + * the y component of the rotation axis + * @param axisZ + * the z component of the rotation axis + * @param angle + * the angle in radians + * @return this + */ + public Quaterniond fromAxisAngleDeg(double axisX, double axisY, double axisZ, double angle) { + return fromAxisAngleRad(axisX, axisY, axisZ, Math.toRadians(angle)); + } + + /** + * Multiply this quaternion by q. + *

+ * If T is this and Q is the given + * quaternion, then the resulting quaternion R is: + *

+ * R = T * Q + *

+ * So, this method uses post-multiplication like the matrix classes, resulting in a + * vector to be transformed by Q first, and then by T. + * + * @param q + * the quaternion to multiply this by + * @return this + */ + public Quaterniond mul(Quaterniondc q) { + return mul(q, this); + } + + public Quaterniond mul(Quaterniondc q, Quaterniond dest) { + return mul(q.x(), q.y(), q.z(), q.w(), dest); + } + + /** + * Multiply this quaternion by the quaternion represented via (qx, qy, qz, qw). + *

+ * If T is this and Q is the given + * quaternion, then the resulting quaternion R is: + *

+ * R = T * Q + *

+ * So, this method uses post-multiplication like the matrix classes, resulting in a + * vector to be transformed by Q first, and then by T. + * + * @param qx + * the x component of the quaternion to multiply this by + * @param qy + * the y component of the quaternion to multiply this by + * @param qz + * the z component of the quaternion to multiply this by + * @param qw + * the w component of the quaternion to multiply this by + * @return this + */ + public Quaterniond mul(double qx, double qy, double qz, double qw) { + return mul(qx, qy, qz, qw, this); + } + + public Quaterniond mul(double qx, double qy, double qz, double qw, Quaterniond dest) { + return dest.set(Math.fma(w, qx, Math.fma(x, qw, Math.fma(y, qz, -z * qy))), + Math.fma(w, qy, Math.fma(-x, qz, Math.fma(y, qw, z * qx))), + Math.fma(w, qz, Math.fma(x, qy, Math.fma(-y, qx, z * qw))), + Math.fma(w, qw, Math.fma(-x, qx, Math.fma(-y, qy, -z * qz)))); + } + + /** + * Pre-multiply this quaternion by q. + *

+ * If T is this and Q is the given quaternion, then the resulting quaternion R is: + *

+ * R = Q * T + *

+ * So, this method uses pre-multiplication, resulting in a vector to be transformed by T first, and then by Q. + * + * @param q + * the quaternion to pre-multiply this by + * @return this + */ + public Quaterniond premul(Quaterniondc q) { + return premul(q, this); + } + + public Quaterniond premul(Quaterniondc q, Quaterniond dest) { + return premul(q.x(), q.y(), q.z(), q.w(), dest); + } + + /** + * Pre-multiply this quaternion by the quaternion represented via (qx, qy, qz, qw). + *

+ * If T is this and Q is the given quaternion, then the resulting quaternion R is: + *

+ * R = Q * T + *

+ * So, this method uses pre-multiplication, resulting in a vector to be transformed by T first, and then by Q. + * + * @param qx + * the x component of the quaternion to multiply this by + * @param qy + * the y component of the quaternion to multiply this by + * @param qz + * the z component of the quaternion to multiply this by + * @param qw + * the w component of the quaternion to multiply this by + * @return this + */ + public Quaterniond premul(double qx, double qy, double qz, double qw) { + return premul(qx, qy, qz, qw, this); + } + + public Quaterniond premul(double qx, double qy, double qz, double qw, Quaterniond dest) { + return dest.set(Math.fma(qw, x, Math.fma(qx, w, Math.fma(qy, z, -qz * y))), + Math.fma(qw, y, Math.fma(-qx, z, Math.fma(qy, w, qz * x))), + Math.fma(qw, z, Math.fma(qx, y, Math.fma(-qy, x, qz * w))), + Math.fma(qw, w, Math.fma(-qx, x, Math.fma(-qy, y, -qz * z)))); + } + + public Vector3d transform(Vector3d vec){ + return transform(vec.x, vec.y, vec.z, vec); + } + + public Vector3d transformInverse(Vector3d vec){ + return transformInverse(vec.x, vec.y, vec.z, vec); + } + + public Vector3d transformUnit(Vector3d vec){ + return transformUnit(vec.x, vec.y, vec.z, vec); + } + + public Vector3d transformInverseUnit(Vector3d vec){ + return transformInverseUnit(vec.x, vec.y, vec.z, vec); + } + + public Vector3d transformPositiveX(Vector3d dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double zw = z * w; + double xy = x * y; + double xz = x * z; + double yw = y * w; + dest.x = ww + xx - zz - yy; + dest.y = xy + zw + zw + xy; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector4d transformPositiveX(Vector4d dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double zw = z * w; + double xy = x * y; + double xz = x * z; + double yw = y * w; + dest.x = ww + xx - zz - yy; + dest.y = xy + zw + zw + xy; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector3d transformUnitPositiveX(Vector3d dest) { + double yy = y * y; + double zz = z * z; + double xy = x * y; + double xz = x * z; + double yw = y * w; + double zw = z * w; + dest.x = 1.0 - yy - yy - zz - zz; + dest.y = xy + zw + xy + zw; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector4d transformUnitPositiveX(Vector4d dest) { + double yy = y * y; + double zz = z * z; + double xy = x * y; + double xz = x * z; + double yw = y * w; + double zw = z * w; + dest.x = 1.0 - yy - yy - zz - zz; + dest.y = xy + zw + xy + zw; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector3d transformPositiveY(Vector3d dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double zw = z * w; + double xy = x * y; + double yz = y * z; + double xw = x * w; + dest.x = -zw + xy - zw + xy; + dest.y = yy - zz + ww - xx; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector4d transformPositiveY(Vector4d dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double zw = z * w; + double xy = x * y; + double yz = y * z; + double xw = x * w; + dest.x = -zw + xy - zw + xy; + dest.y = yy - zz + ww - xx; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector4d transformUnitPositiveY(Vector4d dest) { + double xx = x * x; + double zz = z * z; + double xy = x * y; + double yz = y * z; + double xw = x * w; + double zw = z * w; + dest.x = xy - zw + xy - zw; + dest.y = 1.0 - xx - xx - zz - zz; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector3d transformUnitPositiveY(Vector3d dest) { + double xx = x * x; + double zz = z * z; + double xy = x * y; + double yz = y * z; + double xw = x * w; + double zw = z * w; + dest.x = xy - zw + xy - zw; + dest.y = 1.0 - xx - xx - zz - zz; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector3d transformPositiveZ(Vector3d dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double xz = x * z; + double yw = y * w; + double yz = y * z; + double xw = x * w; + dest.x = yw + xz + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = zz - yy - xx + ww; + return dest; + } + + public Vector4d transformPositiveZ(Vector4d dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double xz = x * z; + double yw = y * w; + double yz = y * z; + double xw = x * w; + dest.x = yw + xz + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = zz - yy - xx + ww; + return dest; + } + + public Vector4d transformUnitPositiveZ(Vector4d dest) { + double xx = x * x; + double yy = y * y; + double xz = x * z; + double yz = y * z; + double xw = x * w; + double yw = y * w; + dest.x = xz + yw + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = 1.0 - xx - xx - yy - yy; + return dest; + } + + public Vector3d transformUnitPositiveZ(Vector3d dest) { + double xx = x * x; + double yy = y * y; + double xz = x * z; + double yz = y * z; + double xw = x * w; + double yw = y * w; + dest.x = xz + yw + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = 1.0 - xx - xx - yy - yy; + return dest; + } + + public Vector4d transform(Vector4d vec){ + return transform(vec, vec); + } + + public Vector4d transformInverse(Vector4d vec){ + return transformInverse(vec, vec); + } + + public Vector3d transform(Vector3dc vec, Vector3d dest) { + return transform(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3d transformInverse(Vector3dc vec, Vector3d dest) { + return transformInverse(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3d transform(double x, double y, double z, Vector3d dest) { + double xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w; + double xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w; + double zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)), + Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)), + Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector3d transformInverse(double x, double y, double z, Vector3d dest) { + double n = 1.0 / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w))); + double qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n; + double xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw; + double xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw; + double zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)), + Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)), + Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector4d transform(Vector4dc vec, Vector4d dest) { + return transform(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4d transformInverse(Vector4dc vec, Vector4d dest) { + return transformInverse(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4d transform(double x, double y, double z, Vector4d dest) { + double xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w; + double xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w; + double zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)), + Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)), + Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z)), dest.w); + } + + public Vector4d transformInverse(double x, double y, double z, Vector4d dest) { + double n = 1.0 / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w))); + double qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n; + double xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw; + double xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw; + double zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)), + Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)), + Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector3f transform(Vector3f vec){ + return transform(vec.x, vec.y, vec.z, vec); + } + + public Vector3f transformInverse(Vector3f vec){ + return transformInverse(vec.x, vec.y, vec.z, vec); + } + + public Vector4d transformUnit(Vector4d vec){ + return transformUnit(vec, vec); + } + + public Vector4d transformInverseUnit(Vector4d vec){ + return transformInverseUnit(vec, vec); + } + + public Vector3d transformUnit(Vector3dc vec, Vector3d dest) { + return transformUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3d transformInverseUnit(Vector3dc vec, Vector3d dest) { + return transformInverseUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3d transformUnit(double x, double y, double z, Vector3d dest) { + double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)), + Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)), + Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector3d transformInverseUnit(double x, double y, double z, Vector3d dest) { + double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)), + Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)), + Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector4d transformUnit(Vector4dc vec, Vector4d dest) { + return transformUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4d transformInverseUnit(Vector4dc vec, Vector4d dest) { + return transformInverseUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4d transformUnit(double x, double y, double z, Vector4d dest) { + double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)), + Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)), + Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z)), + dest.w); + } + + public Vector4d transformInverseUnit(double x, double y, double z, Vector4d dest) { + double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)), + Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)), + Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z)), + dest.w); + } + + public Vector3f transformUnit(Vector3f vec){ + return transformUnit(vec.x, vec.y, vec.z, vec); + } + + public Vector3f transformInverseUnit(Vector3f vec){ + return transformInverseUnit(vec.x, vec.y, vec.z, vec); + } + + public Vector3f transformPositiveX(Vector3f dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double zw = z * w; + double xy = x * y; + double xz = x * z; + double yw = y * w; + dest.x = (float) (ww + xx - zz - yy); + dest.y = (float) (xy + zw + zw + xy); + dest.z = (float) (xz - yw + xz - yw); + return dest; + } + + public Vector4f transformPositiveX(Vector4f dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double zw = z * w; + double xy = x * y; + double xz = x * z; + double yw = y * w; + dest.x = (float) (ww + xx - zz - yy); + dest.y = (float) (xy + zw + zw + xy); + dest.z = (float) (xz - yw + xz - yw); + return dest; + } + + public Vector3f transformUnitPositiveX(Vector3f dest) { + double yy = y * y; + double zz = z * z; + double xy = x * y; + double xz = x * z; + double yw = y * w; + double zw = z * w; + dest.x = (float) (1.0 - yy - yy - zz - zz); + dest.y = (float) (xy + zw + xy + zw); + dest.z = (float) (xz - yw + xz - yw); + return dest; + } + + public Vector4f transformUnitPositiveX(Vector4f dest) { + double yy = y * y; + double zz = z * z; + double xy = x * y; + double xz = x * z; + double yw = y * w; + double zw = z * w; + dest.x = (float) (1.0 - yy - yy - zz - zz); + dest.y = (float) (xy + zw + xy + zw); + dest.z = (float) (xz - yw + xz - yw); + return dest; + } + + public Vector3f transformPositiveY(Vector3f dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double zw = z * w; + double xy = x * y; + double yz = y * z; + double xw = x * w; + dest.x = (float) (-zw + xy - zw + xy); + dest.y = (float) (yy - zz + ww - xx); + dest.z = (float) (yz + yz + xw + xw); + return dest; + } + + public Vector4f transformPositiveY(Vector4f dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double zw = z * w; + double xy = x * y; + double yz = y * z; + double xw = x * w; + dest.x = (float) (-zw + xy - zw + xy); + dest.y = (float) (yy - zz + ww - xx); + dest.z = (float) (yz + yz + xw + xw); + return dest; + } + + public Vector4f transformUnitPositiveY(Vector4f dest) { + double xx = x * x; + double zz = z * z; + double xy = x * y; + double yz = y * z; + double xw = x * w; + double zw = z * w; + dest.x = (float) (xy - zw + xy - zw); + dest.y = (float) (1.0 - xx - xx - zz - zz); + dest.z = (float) (yz + yz + xw + xw); + return dest; + } + + public Vector3f transformUnitPositiveY(Vector3f dest) { + double xx = x * x; + double zz = z * z; + double xy = x * y; + double yz = y * z; + double xw = x * w; + double zw = z * w; + dest.x = (float) (xy - zw + xy - zw); + dest.y = (float) (1.0 - xx - xx - zz - zz); + dest.z = (float) (yz + yz + xw + xw); + return dest; + } + + public Vector3f transformPositiveZ(Vector3f dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double xz = x * z; + double yw = y * w; + double yz = y * z; + double xw = x * w; + dest.x = (float) (yw + xz + xz + yw); + dest.y = (float) (yz + yz - xw - xw); + dest.z = (float) (zz - yy - xx + ww); + return dest; + } + + public Vector4f transformPositiveZ(Vector4f dest) { + double ww = w * w; + double xx = x * x; + double yy = y * y; + double zz = z * z; + double xz = x * z; + double yw = y * w; + double yz = y * z; + double xw = x * w; + dest.x = (float) (yw + xz + xz + yw); + dest.y = (float) (yz + yz - xw - xw); + dest.z = (float) (zz - yy - xx + ww); + return dest; + } + + public Vector4f transformUnitPositiveZ(Vector4f dest) { + double xx = x * x; + double yy = y * y; + double xz = x * z; + double yz = y * z; + double xw = x * w; + double yw = y * w; + dest.x = (float) (xz + yw + xz + yw); + dest.y = (float) (yz + yz - xw - xw); + dest.z = (float) (1.0 - xx - xx - yy - yy); + return dest; + } + + public Vector3f transformUnitPositiveZ(Vector3f dest) { + double xx = x * x; + double yy = y * y; + double xz = x * z; + double yz = y * z; + double xw = x * w; + double yw = y * w; + dest.x = (float) (xz + yw + xz + yw); + dest.y = (float) (yz + yz - xw - xw); + dest.z = (float) (1.0 - xx - xx - yy - yy); + return dest; + } + + public Vector4f transform(Vector4f vec){ + return transform(vec, vec); + } + + public Vector4f transformInverse(Vector4f vec){ + return transformInverse(vec, vec); + } + + public Vector3f transform(Vector3fc vec, Vector3f dest) { + return transform(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3f transformInverse(Vector3fc vec, Vector3f dest) { + return transformInverse(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3f transform(double x, double y, double z, Vector3f dest) { + double xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w; + double xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w; + double zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)), + Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)), + Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector3f transformInverse(double x, double y, double z, Vector3f dest) { + double n = 1.0 / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w))); + double qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n; + double xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw; + double xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw; + double zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)), + Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)), + Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector4f transform(Vector4fc vec, Vector4f dest) { + return transform(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4f transformInverse(Vector4fc vec, Vector4f dest) { + return transformInverse(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4f transform(double x, double y, double z, Vector4f dest) { + double xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w; + double xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w; + double zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww); + return dest.set((float) Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)), + (float) Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)), + (float) Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z)), dest.w); + } + + public Vector4f transformInverse(double x, double y, double z, Vector4f dest) { + double n = 1.0 / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w))); + double qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n; + double xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw; + double xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw; + double zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)), + Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)), + Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z)), dest.w); + } + + public Vector4f transformUnit(Vector4f vec){ + return transformUnit(vec, vec); + } + + public Vector4f transformInverseUnit(Vector4f vec){ + return transformInverseUnit(vec, vec); + } + + public Vector3f transformUnit(Vector3fc vec, Vector3f dest) { + return transformUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3f transformInverseUnit(Vector3fc vec, Vector3f dest) { + return transformInverseUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3f transformUnit(double x, double y, double z, Vector3f dest) { + double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set((float) Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)), + (float) Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)), + (float) Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector3f transformInverseUnit(double x, double y, double z, Vector3f dest) { + double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set((float) Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)), + (float) Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)), + (float) Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector4f transformUnit(Vector4fc vec, Vector4f dest) { + return transformUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4f transformInverseUnit(Vector4fc vec, Vector4f dest) { + return transformInverseUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4f transformUnit(double x, double y, double z, Vector4f dest) { + double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set((float) Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)), + (float) Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)), + (float) Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector4f transformInverseUnit(double x, double y, double z, Vector4f dest) { + double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set((float) Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)), + (float) Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)), + (float) Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Quaterniond invert(Quaterniond dest) { + double invNorm = 1.0 / lengthSquared(); + dest.x = -x * invNorm; + dest.y = -y * invNorm; + dest.z = -z * invNorm; + dest.w = w * invNorm; + return dest; + } + + /** + * Invert this quaternion and {@link #normalize() normalize} it. + *

+ * If this quaternion is already normalized, then {@link #conjugate()} should be used instead. + * + * @see #conjugate() + * + * @return this + */ + public Quaterniond invert() { + return invert(this); + } + + public Quaterniond div(Quaterniondc b, Quaterniond dest) { + double invNorm = 1.0 / Math.fma(b.x(), b.x(), Math.fma(b.y(), b.y(), Math.fma(b.z(), b.z(), b.w() * b.w()))); + double x = -b.x() * invNorm; + double y = -b.y() * invNorm; + double z = -b.z() * invNorm; + double w = b.w() * invNorm; + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Divide this quaternion by b. + *

+ * The division expressed using the inverse is performed in the following way: + *

+ * this = this * b^-1, where b^-1 is the inverse of b. + * + * @param b + * the {@link Quaterniondc} to divide this by + * @return this + */ + public Quaterniond div(Quaterniondc b) { + return div(b, this); + } + + /** + * Conjugate this quaternion. + * + * @return this + */ + public Quaterniond conjugate() { + x = -x; + y = -y; + z = -z; + return this; + } + + public Quaterniond conjugate(Quaterniond dest) { + dest.x = -x; + dest.y = -y; + dest.z = -z; + dest.w = w; + return dest; + } + + /** + * Set this quaternion to the identity. + * + * @return this + */ + public Quaterniond identity() { + x = 0.0; + y = 0.0; + z = 0.0; + w = 1.0; + return this; + } + + public double lengthSquared() { + return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + } + + /** + * Set this quaternion from the supplied euler angles (in radians) with rotation order XYZ. + *

+ * This method is equivalent to calling: rotationX(angleX).rotateY(angleY).rotateZ(angleZ) + *

+ * Reference: this stackexchange answer + * + * @param angleX + * the angle in radians to rotate about x + * @param angleY + * the angle in radians to rotate about y + * @param angleZ + * the angle in radians to rotate about z + * @return this + */ + public Quaterniond rotationXYZ(double angleX, double angleY, double angleZ) { + double sx = Math.sin(angleX * 0.5); + double cx = Math.cosFromSin(sx, angleX * 0.5); + double sy = Math.sin(angleY * 0.5); + double cy = Math.cosFromSin(sy, angleY * 0.5); + double sz = Math.sin(angleZ * 0.5); + double cz = Math.cosFromSin(sz, angleZ * 0.5); + + double cycz = cy * cz; + double sysz = sy * sz; + double sycz = sy * cz; + double cysz = cy * sz; + w = cx*cycz - sx*sysz; + x = sx*cycz + cx*sysz; + y = cx*sycz - sx*cysz; + z = cx*cysz + sx*sycz; + + return this; + } + + /** + * Set this quaternion from the supplied euler angles (in radians) with rotation order ZYX. + *

+ * This method is equivalent to calling: rotationZ(angleZ).rotateY(angleY).rotateX(angleX) + *

+ * Reference: this stackexchange answer + * + * @param angleX + * the angle in radians to rotate about x + * @param angleY + * the angle in radians to rotate about y + * @param angleZ + * the angle in radians to rotate about z + * @return this + */ + public Quaterniond rotationZYX(double angleZ, double angleY, double angleX) { + double sx = Math.sin(angleX * 0.5); + double cx = Math.cosFromSin(sx, angleX * 0.5); + double sy = Math.sin(angleY * 0.5); + double cy = Math.cosFromSin(sy, angleY * 0.5); + double sz = Math.sin(angleZ * 0.5); + double cz = Math.cosFromSin(sz, angleZ * 0.5); + + double cycz = cy * cz; + double sysz = sy * sz; + double sycz = sy * cz; + double cysz = cy * sz; + w = cx*cycz + sx*sysz; + x = sx*cycz - cx*sysz; + y = cx*sycz + sx*cysz; + z = cx*cysz - sx*sycz; + + return this; + } + + /** + * Set this quaternion from the supplied euler angles (in radians) with rotation order YXZ. + *

+ * This method is equivalent to calling: rotationY(angleY).rotateX(angleX).rotateZ(angleZ) + *

+ * Reference: https://en.wikipedia.org + * + * @param angleY + * the angle in radians to rotate about y + * @param angleX + * the angle in radians to rotate about x + * @param angleZ + * the angle in radians to rotate about z + * @return this + */ + public Quaterniond rotationYXZ(double angleY, double angleX, double angleZ) { + double sx = Math.sin(angleX * 0.5); + double cx = Math.cosFromSin(sx, angleX * 0.5); + double sy = Math.sin(angleY * 0.5); + double cy = Math.cosFromSin(sy, angleY * 0.5); + double sz = Math.sin(angleZ * 0.5); + double cz = Math.cosFromSin(sz, angleZ * 0.5); + + double x = cy * sx; + double y = sy * cx; + double z = sy * sx; + double w = cy * cx; + this.x = x * cz + y * sz; + this.y = y * cz - x * sz; + this.z = w * sz - z * cz; + this.w = w * cz + z * sz; + + return this; + } + + /** + * Interpolate between this {@link #normalize() unit} quaternion and the specified + * target {@link #normalize() unit} quaternion using spherical linear interpolation using the specified interpolation factor alpha. + *

+ * This method resorts to non-spherical linear interpolation when the absolute dot product between this and target is + * below 1E-6. + * + * @param target + * the target of the interpolation, which should be reached with alpha = 1.0 + * @param alpha + * the interpolation factor, within [0..1] + * @return this + */ + public Quaterniond slerp(Quaterniondc target, double alpha) { + return slerp(target, alpha, this); + } + + public Quaterniond slerp(Quaterniondc target, double alpha, Quaterniond dest) { + double cosom = Math.fma(x, target.x(), Math.fma(y, target.y(), Math.fma(z, target.z(), w * target.w()))); + double absCosom = Math.abs(cosom); + double scale0, scale1; + if (1.0 - absCosom > 1E-6) { + double sinSqr = 1.0 - absCosom * absCosom; + double sinom = Math.invsqrt(sinSqr); + double omega = Math.atan2(sinSqr * sinom, absCosom); + scale0 = Math.sin((1.0 - alpha) * omega) * sinom; + scale1 = Math.sin(alpha * omega) * sinom; + } else { + scale0 = 1.0 - alpha; + scale1 = alpha; + } + scale1 = cosom >= 0.0 ? scale1 : -scale1; + dest.x = Math.fma(scale0, x, scale1 * target.x()); + dest.y = Math.fma(scale0, y, scale1 * target.y()); + dest.z = Math.fma(scale0, z, scale1 * target.z()); + dest.w = Math.fma(scale0, w, scale1 * target.w()); + return dest; + } + + /** + * Interpolate between all of the quaternions given in qs via spherical linear interpolation using the specified interpolation factors weights, + * and store the result in dest. + *

+ * This method will interpolate between each two successive quaternions via {@link #slerp(Quaterniondc, double)} using their relative interpolation weights. + *

+ * This method resorts to non-spherical linear interpolation when the absolute dot product of any two interpolated quaternions is below 1E-6f. + *

+ * Reference: http://gamedev.stackexchange.com/ + * + * @param qs + * the quaternions to interpolate over + * @param weights + * the weights of each individual quaternion in qs + * @param dest + * will hold the result + * @return dest + */ + public static Quaterniondc slerp(Quaterniond[] qs, double[] weights, Quaterniond dest) { + dest.set(qs[0]); + double w = weights[0]; + for (int i = 1; i < qs.length; i++) { + double w0 = w; + double w1 = weights[i]; + double rw1 = w1 / (w0 + w1); + w += w1; + dest.slerp(qs[i], rw1); + } + return dest; + } + + /** + * Apply scaling to this quaternion, which results in any vector transformed by this quaternion to change + * its length by the given factor. + * + * @param factor + * the scaling factor + * @return this + */ + public Quaterniond scale(double factor) { + return scale(factor, this); + } + + public Quaterniond scale(double factor, Quaterniond dest) { + double sqrt = Math.sqrt(factor); + dest.x = sqrt * x; + dest.y = sqrt * y; + dest.z = sqrt * z; + dest.w = sqrt * w; + return dest; + } + + /** + * Set this quaternion to represent scaling, which results in a transformed vector to change + * its length by the given factor. + * + * @param factor + * the scaling factor + * @return this + */ + public Quaterniond scaling(double factor) { + double sqrt = Math.sqrt(factor); + this.x = 0.0; + this.y = 0.0; + this.z = 0.0; + this.w = sqrt; + return this; + } + + /** + * Integrate the rotation given by the angular velocity (vx, vy, vz) around the x, y and z axis, respectively, + * with respect to the given elapsed time delta dt and add the differentiate rotation to the rotation represented by this quaternion. + *

+ * This method pre-multiplies the rotation given by dt and (vx, vy, vz) by this, so + * the angular velocities are always relative to the local coordinate system of the rotation represented by this quaternion. + *

+ * This method is equivalent to calling: rotateLocal(dt * vx, dt * vy, dt * vz) + *

+ * Reference: http://physicsforgames.blogspot.de/ + * + * @param dt + * the delta time + * @param vx + * the angular velocity around the x axis + * @param vy + * the angular velocity around the y axis + * @param vz + * the angular velocity around the z axis + * @return this + */ + public Quaterniond integrate(double dt, double vx, double vy, double vz) { + return integrate(dt, vx, vy, vz, this); + } + + public Quaterniond integrate(double dt, double vx, double vy, double vz, Quaterniond dest) { + double thetaX = dt * vx * 0.5; + double thetaY = dt * vy * 0.5; + double thetaZ = dt * vz * 0.5; + double thetaMagSq = thetaX * thetaX + thetaY * thetaY + thetaZ * thetaZ; + double s; + double dqX, dqY, dqZ, dqW; + if (thetaMagSq * thetaMagSq / 24.0 < 1E-8) { + dqW = 1.0 - thetaMagSq * 0.5; + s = 1.0 - thetaMagSq / 6.0; + } else { + double thetaMag = Math.sqrt(thetaMagSq); + double sin = Math.sin(thetaMag); + s = sin / thetaMag; + dqW = Math.cosFromSin(sin, thetaMag); + } + dqX = thetaX * s; + dqY = thetaY * s; + dqZ = thetaZ * s; + /* Pre-multiplication */ + return dest.set(Math.fma(dqW, x, Math.fma(dqX, w, Math.fma(dqY, z, -dqZ * y))), + Math.fma(dqW, y, Math.fma(-dqX, z, Math.fma(dqY, w, dqZ * x))), + Math.fma(dqW, z, Math.fma(dqX, y, Math.fma(-dqY, x, dqZ * w))), + Math.fma(dqW, w, Math.fma(-dqX, x, Math.fma(-dqY, y, -dqZ * z)))); + } + + /** + * Compute a linear (non-spherical) interpolation of this and the given quaternion q + * and store the result in this. + * + * @param q + * the other quaternion + * @param factor + * the interpolation factor. It is between 0.0 and 1.0 + * @return this + */ + public Quaterniond nlerp(Quaterniondc q, double factor) { + return nlerp(q, factor, this); + } + + public Quaterniond nlerp(Quaterniondc q, double factor, Quaterniond dest) { + double cosom = Math.fma(x, q.x(), Math.fma(y, q.y(), Math.fma(z, q.z(), w * q.w()))); + double scale0 = 1.0 - factor; + double scale1 = (cosom >= 0.0) ? factor : -factor; + dest.x = Math.fma(scale0, x, scale1 * q.x()); + dest.y = Math.fma(scale0, y, scale1 * q.y()); + dest.z = Math.fma(scale0, z, scale1 * q.z()); + dest.w = Math.fma(scale0, w, scale1 * q.w()); + double s = Math.invsqrt(Math.fma(dest.x, dest.x, Math.fma(dest.y, dest.y, Math.fma(dest.z, dest.z, dest.w * dest.w)))); + dest.x *= s; + dest.y *= s; + dest.z *= s; + dest.w *= s; + return dest; + } + + /** + * Interpolate between all of the quaternions given in qs via non-spherical linear interpolation using the + * specified interpolation factors weights, and store the result in dest. + *

+ * This method will interpolate between each two successive quaternions via {@link #nlerp(Quaterniondc, double)} + * using their relative interpolation weights. + *

+ * Reference: http://gamedev.stackexchange.com/ + * + * @param qs + * the quaternions to interpolate over + * @param weights + * the weights of each individual quaternion in qs + * @param dest + * will hold the result + * @return dest + */ + public static Quaterniondc nlerp(Quaterniond[] qs, double[] weights, Quaterniond dest) { + dest.set(qs[0]); + double w = weights[0]; + for (int i = 1; i < qs.length; i++) { + double w0 = w; + double w1 = weights[i]; + double rw1 = w1 / (w0 + w1); + w += w1; + dest.nlerp(qs[i], rw1); + } + return dest; + } + + public Quaterniond nlerpIterative(Quaterniondc q, double alpha, double dotThreshold, Quaterniond dest) { + double q1x = x, q1y = y, q1z = z, q1w = w; + double q2x = q.x(), q2y = q.y(), q2z = q.z(), q2w = q.w(); + double dot = Math.fma(q1x, q2x, Math.fma(q1y, q2y, Math.fma(q1z, q2z, q1w * q2w))); + double absDot = Math.abs(dot); + if (1.0 - 1E-6 < absDot) { + return dest.set(this); + } + double alphaN = alpha; + while (absDot < dotThreshold) { + double scale0 = 0.5; + double scale1 = dot >= 0.0 ? 0.5 : -0.5; + if (alphaN < 0.5) { + q2x = Math.fma(scale0, q2x, scale1 * q1x); + q2y = Math.fma(scale0, q2y, scale1 * q1y); + q2z = Math.fma(scale0, q2z, scale1 * q1z); + q2w = Math.fma(scale0, q2w, scale1 * q1w); + float s = (float) Math.invsqrt(Math.fma(q2x, q2x, Math.fma(q2y, q2y, Math.fma(q2z, q2z, q2w * q2w)))); + q2x *= s; + q2y *= s; + q2z *= s; + q2w *= s; + alphaN = alphaN + alphaN; + } else { + q1x = Math.fma(scale0, q1x, scale1 * q2x); + q1y = Math.fma(scale0, q1y, scale1 * q2y); + q1z = Math.fma(scale0, q1z, scale1 * q2z); + q1w = Math.fma(scale0, q1w, scale1 * q2w); + float s = (float) Math.invsqrt(Math.fma(q1x, q1x, Math.fma(q1y, q1y, Math.fma(q1z, q1z, q1w * q1w)))); + q1x *= s; + q1y *= s; + q1z *= s; + q1w *= s; + alphaN = alphaN + alphaN - 1.0; + } + dot = Math.fma(q1x, q2x, Math.fma(q1y, q2y, Math.fma(q1z, q2z, q1w * q2w))); + absDot = Math.abs(dot); + } + double scale0 = 1.0 - alphaN; + double scale1 = dot >= 0.0 ? alphaN : -alphaN; + double resX = Math.fma(scale0, q1x, scale1 * q2x); + double resY = Math.fma(scale0, q1y, scale1 * q2y); + double resZ = Math.fma(scale0, q1z, scale1 * q2z); + double resW = Math.fma(scale0, q1w, scale1 * q2w); + double s = Math.invsqrt(Math.fma(resX, resX, Math.fma(resY, resY, Math.fma(resZ, resZ, resW * resW)))); + dest.x = resX * s; + dest.y = resY * s; + dest.z = resZ * s; + dest.w = resW * s; + return dest; + } + + /** + * Compute linear (non-spherical) interpolations of this and the given quaternion q + * iteratively and store the result in this. + *

+ * This method performs a series of small-step nlerp interpolations to avoid doing a costly spherical linear interpolation, like + * {@link #slerp(Quaterniondc, double, Quaterniond) slerp}, + * by subdividing the rotation arc between this and q via non-spherical linear interpolations as long as + * the absolute dot product of this and q is greater than the given dotThreshold parameter. + *

+ * Thanks to @theagentd at http://www.java-gaming.org/ for providing the code. + * + * @param q + * the other quaternion + * @param alpha + * the interpolation factor, between 0.0 and 1.0 + * @param dotThreshold + * the threshold for the dot product of this and q above which this method performs another iteration + * of a small-step linear interpolation + * @return this + */ + public Quaterniond nlerpIterative(Quaterniondc q, double alpha, double dotThreshold) { + return nlerpIterative(q, alpha, dotThreshold, this); + } + + /** + * Interpolate between all of the quaternions given in qs via iterative non-spherical linear interpolation using the + * specified interpolation factors weights, and store the result in dest. + *

+ * This method will interpolate between each two successive quaternions via {@link #nlerpIterative(Quaterniondc, double, double)} + * using their relative interpolation weights. + *

+ * Reference: http://gamedev.stackexchange.com/ + * + * @param qs + * the quaternions to interpolate over + * @param weights + * the weights of each individual quaternion in qs + * @param dotThreshold + * the threshold for the dot product of each two interpolated quaternions above which {@link #nlerpIterative(Quaterniondc, double, double)} performs another iteration + * of a small-step linear interpolation + * @param dest + * will hold the result + * @return dest + */ + public static Quaterniond nlerpIterative(Quaterniondc[] qs, double[] weights, double dotThreshold, Quaterniond dest) { + dest.set(qs[0]); + double w = weights[0]; + for (int i = 1; i < qs.length; i++) { + double w0 = w; + double w1 = weights[i]; + double rw1 = w1 / (w0 + w1); + w += w1; + dest.nlerpIterative(qs[i], rw1, dotThreshold); + } + return dest; + } + + /** + * Apply a rotation to this quaternion that maps the given direction to the positive Z axis. + *

+ * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain + * parallel to the plane spanned by the up and dir vectors. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: http://answers.unity3d.com + * + * @see #lookAlong(double, double, double, double, double, double, Quaterniond) + * + * @param dir + * the direction to map to the positive Z axis + * @param up + * the vector which will be mapped to a vector parallel to the plane + * spanned by the given dir and up + * @return this + */ + public Quaterniond lookAlong(Vector3dc dir, Vector3dc up) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + public Quaterniond lookAlong(Vector3dc dir, Vector3dc up, Quaterniond dest) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a rotation to this quaternion that maps the given direction to the positive Z axis. + *

+ * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain + * parallel to the plane spanned by the up and dir vectors. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: http://answers.unity3d.com + * + * @see #lookAlong(double, double, double, double, double, double, Quaterniond) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Quaterniond lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { + return lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + public Quaterniond lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Quaterniond dest) { + // Normalize direction + double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + double dirnX = -dirX * invDirLength; + double dirnY = -dirY * invDirLength; + double dirnZ = -dirZ * invDirLength; + // left = up x dir + double leftX, leftY, leftZ; + leftX = upY * dirnZ - upZ * dirnY; + leftY = upZ * dirnX - upX * dirnZ; + leftZ = upX * dirnY - upY * dirnX; + // normalize left + double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + double upnX = dirnY * leftZ - dirnZ * leftY; + double upnY = dirnZ * leftX - dirnX * leftZ; + double upnZ = dirnX * leftY - dirnY * leftX; + + /* Convert orthonormal basis vectors to quaternion */ + double x, y, z, w; + double t; + double tr = leftX + upnY + dirnZ; + if (tr >= 0.0) { + t = Math.sqrt(tr + 1.0); + w = t * 0.5; + t = 0.5 / t; + x = (dirnY - upnZ) * t; + y = (leftZ - dirnX) * t; + z = (upnX - leftY) * t; + } else { + if (leftX > upnY && leftX > dirnZ) { + t = Math.sqrt(1.0 + leftX - upnY - dirnZ); + x = t * 0.5; + t = 0.5 / t; + y = (leftY + upnX) * t; + z = (dirnX + leftZ) * t; + w = (dirnY - upnZ) * t; + } else if (upnY > dirnZ) { + t = Math.sqrt(1.0 + upnY - leftX - dirnZ); + y = t * 0.5; + t = 0.5 / t; + x = (leftY + upnX) * t; + z = (upnZ + dirnY) * t; + w = (leftZ - dirnX) * t; + } else { + t = Math.sqrt(1.0 + dirnZ - leftX - upnY); + z = t * 0.5; + t = 0.5 / t; + x = (dirnX + leftZ) * t; + y = (upnZ + dirnY) * t; + w = (upnX - leftY) * t; + } + } + /* Multiply */ + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Return a string representation of this quaternion. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this quaternion by formatting the components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the quaternion components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + " " + Runtime.format(z, formatter) + " " + Runtime.format(w, formatter) + ")"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(x); + out.writeDouble(y); + out.writeDouble(z); + out.writeDouble(w); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + x = in.readDouble(); + y = in.readDouble(); + z = in.readDouble(); + w = in.readDouble(); + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(w); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(x); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(y); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(z); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Quaterniond other = (Quaterniond) obj; + if (Double.doubleToLongBits(w) != Double.doubleToLongBits(other.w)) + return false; + if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) + return false; + if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) + return false; + if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z)) + return false; + return true; + } + + /** + * Compute the difference between this and the other quaternion + * and store the result in this. + *

+ * The difference is the rotation that has to be applied to get from + * this rotation to other. If T is this, Q + * is other and D is the computed difference, then the following equation holds: + *

+ * T * D = Q + *

+ * It is defined as: D = T^-1 * Q, where T^-1 denotes the {@link #invert() inverse} of T. + * + * @param other + * the other quaternion + * @return this + */ + public Quaterniond difference(Quaterniondc other) { + return difference(other, this); + } + + public Quaterniond difference(Quaterniondc other, Quaterniond dest) { + double invNorm = 1.0 / lengthSquared(); + double x = -this.x * invNorm; + double y = -this.y * invNorm; + double z = -this.z * invNorm; + double w = this.w * invNorm; + dest.set(Math.fma(w, other.x(), Math.fma(x, other.w(), Math.fma(y, other.z(), -z * other.y()))), + Math.fma(w, other.y(), Math.fma(-x, other.z(), Math.fma(y, other.w(), z * other.x()))), + Math.fma(w, other.z(), Math.fma(x, other.y(), Math.fma(-y, other.x(), z * other.w()))), + Math.fma(w, other.w(), Math.fma(-x, other.x(), Math.fma(-y, other.y(), -z * other.z())))); + return dest; + } + + /** + * Set this quaternion to a rotation that rotates the fromDir vector to point along toDir. + *

+ * Since there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * Reference: stackoverflow.com + * + * @param fromDirX + * the x-coordinate of the direction to rotate into the destination direction + * @param fromDirY + * the y-coordinate of the direction to rotate into the destination direction + * @param fromDirZ + * the z-coordinate of the direction to rotate into the destination direction + * @param toDirX + * the x-coordinate of the direction to rotate to + * @param toDirY + * the y-coordinate of the direction to rotate to + * @param toDirZ + * the z-coordinate of the direction to rotate to + * @return this + */ + public Quaterniond rotationTo(double fromDirX, double fromDirY, double fromDirZ, double toDirX, double toDirY, double toDirZ) { + double fn = Math.invsqrt(Math.fma(fromDirX, fromDirX, Math.fma(fromDirY, fromDirY, fromDirZ * fromDirZ))); + double tn = Math.invsqrt(Math.fma(toDirX, toDirX, Math.fma(toDirY, toDirY, toDirZ * toDirZ))); + double fx = fromDirX * fn, fy = fromDirY * fn, fz = fromDirZ * fn; + double tx = toDirX * tn, ty = toDirY * tn, tz = toDirZ * tn; + double dot = fx * tx + fy * ty + fz * tz; + double x, y, z, w; + if (dot < -1.0 + 1E-6) { + x = fy; + y = -fx; + z = 0.0; + w = 0.0; + if (x * x + y * y == 0.0) { + x = 0.0; + y = fz; + z = -fy; + w = 0.0; + } + this.x = x; + this.y = y; + this.z = z; + this.w = 0; + } else { + double sd2 = Math.sqrt((1.0 + dot) * 2.0); + double isd2 = 1.0 / sd2; + double cx = fy * tz - fz * ty; + double cy = fz * tx - fx * tz; + double cz = fx * ty - fy * tx; + x = cx * isd2; + y = cy * isd2; + z = cz * isd2; + w = sd2 * 0.5; + double n2 = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + this.x = x * n2; + this.y = y * n2; + this.z = z * n2; + this.w = w * n2; + } + return this; + } + + /** + * Set this quaternion to a rotation that rotates the fromDir vector to point along toDir. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + * + * @see #rotationTo(double, double, double, double, double, double) + * + * @param fromDir + * the starting direction + * @param toDir + * the destination direction + * @return this + */ + public Quaterniond rotationTo(Vector3dc fromDir, Vector3dc toDir) { + return rotationTo(fromDir.x(), fromDir.y(), fromDir.z(), toDir.x(), toDir.y(), toDir.z()); + } + + public Quaterniond rotateTo(double fromDirX, double fromDirY, double fromDirZ, + double toDirX, double toDirY, double toDirZ, Quaterniond dest) { + double fn = Math.invsqrt(Math.fma(fromDirX, fromDirX, Math.fma(fromDirY, fromDirY, fromDirZ * fromDirZ))); + double tn = Math.invsqrt(Math.fma(toDirX, toDirX, Math.fma(toDirY, toDirY, toDirZ * toDirZ))); + double fx = fromDirX * fn, fy = fromDirY * fn, fz = fromDirZ * fn; + double tx = toDirX * tn, ty = toDirY * tn, tz = toDirZ * tn; + double dot = fx * tx + fy * ty + fz * tz; + double x, y, z, w; + if (dot < -1.0 + 1E-6) { + x = fy; + y = -fx; + z = 0.0; + w = 0.0; + if (x * x + y * y == 0.0) { + x = 0.0; + y = fz; + z = -fy; + w = 0.0; + } + } else { + double sd2 = Math.sqrt((1.0 + dot) * 2.0); + double isd2 = 1.0 / sd2; + double cx = fy * tz - fz * ty; + double cy = fz * tx - fx * tz; + double cz = fx * ty - fy * tx; + x = cx * isd2; + y = cy * isd2; + z = cz * isd2; + w = sd2 * 0.5; + double n2 = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + x *= n2; + y *= n2; + z *= n2; + w *= n2; + } + /* Multiply */ + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Set this {@link Quaterniond} to a rotation of the given angle in radians about the supplied + * axis, all of which are specified via the {@link AxisAngle4f}. + * + * @see #rotationAxis(double, double, double, double) + * + * @param axisAngle + * the {@link AxisAngle4f} giving the rotation angle in radians and the axis to rotate about + * @return this + */ + public Quaterniond rotationAxis(AxisAngle4f axisAngle) { + return rotationAxis(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this quaternion to a rotation of the given angle in radians about the supplied axis. + * + * @param angle + * the rotation angle in radians + * @param axisX + * the x-coordinate of the rotation axis + * @param axisY + * the y-coordinate of the rotation axis + * @param axisZ + * the z-coordinate of the rotation axis + * @return this + */ + public Quaterniond rotationAxis(double angle, double axisX, double axisY, double axisZ) { + double hangle = angle / 2.0; + double sinAngle = Math.sin(hangle); + double invVLength = Math.invsqrt(axisX * axisX + axisY * axisY + axisZ * axisZ); + return set(axisX * invVLength * sinAngle, + axisY * invVLength * sinAngle, + axisZ * invVLength * sinAngle, + Math.cosFromSin(sinAngle, hangle)); + } + + /** + * Set this quaternion to represent a rotation of the given radians about the x axis. + * + * @param angle + * the angle in radians to rotate about the x axis + * @return this + */ + public Quaterniond rotationX(double angle) { + double sin = Math.sin(angle * 0.5); + double cos = Math.cosFromSin(sin, angle * 0.5); + return set(sin, 0, cos, 0); + } + + /** + * Set this quaternion to represent a rotation of the given radians about the y axis. + * + * @param angle + * the angle in radians to rotate about the y axis + * @return this + */ + public Quaterniond rotationY(double angle) { + double sin = Math.sin(angle * 0.5); + double cos = Math.cosFromSin(sin, angle * 0.5); + return set(0, sin, 0, cos); + } + + /** + * Set this quaternion to represent a rotation of the given radians about the z axis. + * + * @param angle + * the angle in radians to rotate about the z axis + * @return this + */ + public Quaterniond rotationZ(double angle) { + double sin = Math.sin(angle * 0.5); + double cos = Math.cosFromSin(sin, angle * 0.5); + return set(0, 0, sin, cos); + } + + /** + * Apply a rotation to this that rotates the fromDir vector to point along toDir. + *

+ * Since there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateTo(double, double, double, double, double, double, Quaterniond) + * + * @param fromDirX + * the x-coordinate of the direction to rotate into the destination direction + * @param fromDirY + * the y-coordinate of the direction to rotate into the destination direction + * @param fromDirZ + * the z-coordinate of the direction to rotate into the destination direction + * @param toDirX + * the x-coordinate of the direction to rotate to + * @param toDirY + * the y-coordinate of the direction to rotate to + * @param toDirZ + * the z-coordinate of the direction to rotate to + * @return this + */ + public Quaterniond rotateTo(double fromDirX, double fromDirY, double fromDirZ, double toDirX, double toDirY, double toDirZ) { + return rotateTo(fromDirX, fromDirY, fromDirZ, toDirX, toDirY, toDirZ, this); + } + + public Quaterniond rotateTo(Vector3dc fromDir, Vector3dc toDir, Quaterniond dest) { + return rotateTo(fromDir.x(), fromDir.y(), fromDir.z(), toDir.x(), toDir.y(), toDir.z(), dest); + } + + /** + * Apply a rotation to this that rotates the fromDir vector to point along toDir. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateTo(double, double, double, double, double, double, Quaterniond) + * + * @param fromDir + * the starting direction + * @param toDir + * the destination direction + * @return this + */ + public Quaterniond rotateTo(Vector3dc fromDir, Vector3dc toDir) { + return rotateTo(fromDir.x(), fromDir.y(), fromDir.z(), toDir.x(), toDir.y(), toDir.z(), this); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the x axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the x axis + * @return this + */ + public Quaterniond rotateX(double angle) { + return rotateX(angle, this); + } + + public Quaterniond rotateX(double angle, Quaterniond dest) { + double sin = Math.sin(angle * 0.5); + double cos = Math.cosFromSin(sin, angle * 0.5); + return dest.set(w * sin + x * cos, + y * cos + z * sin, + z * cos - y * sin, + w * cos - x * sin); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the y axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the y axis + * @return this + */ + public Quaterniond rotateY(double angle) { + return rotateY(angle, this); + } + + public Quaterniond rotateY(double angle, Quaterniond dest) { + double sin = Math.sin(angle * 0.5); + double cos = Math.cosFromSin(sin, angle * 0.5); + return dest.set(x * cos - z * sin, + w * sin + y * cos, + x * sin + z * cos, + w * cos - y * sin); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the z axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the z axis + * @return this + */ + public Quaterniond rotateZ(double angle) { + return rotateZ(angle, this); + } + + public Quaterniond rotateZ(double angle, Quaterniond dest) { + double sin = Math.sin(angle * 0.5); + double cos = Math.cosFromSin(sin, angle * 0.5); + return dest.set(x * cos + y * sin, + y * cos - x * sin, + w * sin + z * cos, + w * cos - z * sin); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the local x axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local x axis + * @return this + */ + public Quaterniond rotateLocalX(double angle) { + return rotateLocalX(angle, this); + } + + public Quaterniond rotateLocalX(double angle, Quaterniond dest) { + double hangle = angle * 0.5; + double s = Math.sin(hangle); + double c = Math.cosFromSin(s, hangle); + dest.set(c * x + s * w, + c * y - s * z, + c * z + s * y, + c * w - s * x); + return dest; + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the local y axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local y axis + * @return this + */ + public Quaterniond rotateLocalY(double angle) { + return rotateLocalY(angle, this); + } + + public Quaterniond rotateLocalY(double angle, Quaterniond dest) { + double hangle = angle * 0.5; + double s = Math.sin(hangle); + double c = Math.cosFromSin(s, hangle); + dest.set(c * x + s * z, + c * y + s * w, + c * z - s * x, + c * w - s * y); + return dest; + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the local z axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local z axis + * @return this + */ + public Quaterniond rotateLocalZ(double angle) { + return rotateLocalZ(angle, this); + } + + public Quaterniond rotateLocalZ(double angle, Quaterniond dest) { + double hangle = angle * 0.5; + double s = Math.sin(hangle); + double c = Math.cosFromSin(s, hangle); + dest.set(c * x - s * y, + c * y + s * x, + c * z + s * w, + c * w - s * z); + return dest; + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles using rotation sequence XYZ. + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleX + * the angle in radians to rotate about the x axis + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleZ + * the angle in radians to rotate about the z axis + * @return this + */ + public Quaterniond rotateXYZ(double angleX, double angleY, double angleZ) { + return rotateXYZ(angleX, angleY, angleZ, this); + } + + public Quaterniond rotateXYZ(double angleX, double angleY, double angleZ, Quaterniond dest) { + double sx = Math.sin(angleX * 0.5); + double cx = Math.cosFromSin(sx, angleX * 0.5); + double sy = Math.sin(angleY * 0.5); + double cy = Math.cosFromSin(sy, angleY * 0.5); + double sz = Math.sin(angleZ * 0.5); + double cz = Math.cosFromSin(sz, angleZ * 0.5); + + double cycz = cy * cz; + double sysz = sy * sz; + double sycz = sy * cz; + double cysz = cy * sz; + double w = cx*cycz - sx*sysz; + double x = sx*cycz + cx*sysz; + double y = cx*sycz - sx*cysz; + double z = cx*cysz + sx*sycz; + // right-multiply + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles, using the rotation sequence ZYX. + *

+ * This method is equivalent to calling: rotateZ(angleZ).rotateY(angleY).rotateX(angleX) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleZ + * the angle in radians to rotate about the z axis + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleX + * the angle in radians to rotate about the x axis + * @return this + */ + public Quaterniond rotateZYX(double angleZ, double angleY, double angleX) { + return rotateZYX(angleZ, angleY, angleX, this); + } + + public Quaterniond rotateZYX(double angleZ, double angleY, double angleX, Quaterniond dest) { + double sx = Math.sin(angleX * 0.5); + double cx = Math.cosFromSin(sx, angleX * 0.5); + double sy = Math.sin(angleY * 0.5); + double cy = Math.cosFromSin(sy, angleY * 0.5); + double sz = Math.sin(angleZ * 0.5); + double cz = Math.cosFromSin(sz, angleZ * 0.5); + + double cycz = cy * cz; + double sysz = sy * sz; + double sycz = sy * cz; + double cysz = cy * sz; + double w = cx*cycz + sx*sysz; + double x = sx*cycz - cx*sysz; + double y = cx*sycz + sx*cysz; + double z = cx*cysz - sx*sycz; + // right-multiply + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles, using the rotation sequence YXZ. + *

+ * This method is equivalent to calling: rotateY(angleY).rotateX(angleX).rotateZ(angleZ) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleX + * the angle in radians to rotate about the x axis + * @param angleZ + * the angle in radians to rotate about the z axis + * @return this + */ + public Quaterniond rotateYXZ(double angleY, double angleX, double angleZ) { + return rotateYXZ(angleY, angleX, angleZ, this); + } + + public Quaterniond rotateYXZ(double angleY, double angleX, double angleZ, Quaterniond dest) { + double sx = Math.sin(angleX * 0.5); + double cx = Math.cosFromSin(sx, angleX * 0.5); + double sy = Math.sin(angleY * 0.5); + double cy = Math.cosFromSin(sy, angleY * 0.5); + double sz = Math.sin(angleZ * 0.5); + double cz = Math.cosFromSin(sz, angleZ * 0.5); + + double yx = cy * sx; + double yy = sy * cx; + double yz = sy * sx; + double yw = cy * cx; + double x = yx * cz + yy * sz; + double y = yy * cz - yx * sz; + double z = yw * sz - yz * cz; + double w = yw * cz + yz * sz; + // right-multiply + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + public Vector3d getEulerAnglesXYZ(Vector3d eulerAngles) { + eulerAngles.x = Math.atan2(x * w - y * z, 0.5 - x * x - y * y); + eulerAngles.y = Math.safeAsin(2.0 * (x * z + y * w)); + eulerAngles.z = Math.atan2(z * w - x * y, 0.5 - y * y - z * z); + return eulerAngles; + } + + public Vector3d getEulerAnglesZYX(Vector3d eulerAngles) { + eulerAngles.x = Math.atan2(y * z + w * x, 0.5 - x * x + y * y); + eulerAngles.y = Math.safeAsin(-2.0 * (x * z - w * y)); + eulerAngles.z = Math.atan2(x * y + w * z, 0.5 - y * y - z * z); + return eulerAngles; + } + + public Quaterniond rotateAxis(double angle, double axisX, double axisY, double axisZ, Quaterniond dest) { + double hangle = angle / 2.0; + double sinAngle = Math.sin(hangle); + double invVLength = Math.invsqrt(Math.fma(axisX, axisX, Math.fma(axisY, axisY, axisZ * axisZ))); + double rx = axisX * invVLength * sinAngle; + double ry = axisY * invVLength * sinAngle; + double rz = axisZ * invVLength * sinAngle; + double rw = Math.cosFromSin(sinAngle, hangle); + return dest.set(Math.fma(this.w, rx, Math.fma(this.x, rw, Math.fma(this.y, rz, -this.z * ry))), + Math.fma(this.w, ry, Math.fma(-this.x, rz, Math.fma(this.y, rw, this.z * rx))), + Math.fma(this.w, rz, Math.fma(this.x, ry, Math.fma(-this.y, rx, this.z * rw))), + Math.fma(this.w, rw, Math.fma(-this.x, rx, Math.fma(-this.y, ry, -this.z * rz)))); + } + + public Quaterniond rotateAxis(double angle, Vector3dc axis, Quaterniond dest) { + return rotateAxis(angle, axis.x(), axis.y(), axis.z(), dest); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the specified axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateAxis(double, double, double, double, Quaterniond) + * + * @param angle + * the angle in radians to rotate about the specified axis + * @param axis + * the rotation axis + * @return this + */ + public Quaterniond rotateAxis(double angle, Vector3dc axis) { + return rotateAxis(angle, axis.x(), axis.y(), axis.z(), this); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the specified axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateAxis(double, double, double, double, Quaterniond) + * + * @param angle + * the angle in radians to rotate about the specified axis + * @param axisX + * the x coordinate of the rotation axis + * @param axisY + * the y coordinate of the rotation axis + * @param axisZ + * the z coordinate of the rotation axis + * @return this + */ + public Quaterniond rotateAxis(double angle, double axisX, double axisY, double axisZ) { + return rotateAxis(angle, axisX, axisY, axisZ, this); + } + + public Vector3d positiveX(Vector3d dir) { + double invNorm = 1.0 / lengthSquared(); + double nx = -x * invNorm; + double ny = -y * invNorm; + double nz = -z * invNorm; + double nw = w * invNorm; + double dy = ny + ny; + double dz = nz + nz; + dir.x = -ny * dy - nz * dz + 1.0; + dir.y = nx * dy + nw * dz; + dir.z = nx * dz - nw * dy; + return dir; + } + + public Vector3d normalizedPositiveX(Vector3d dir) { + double dy = y + y; + double dz = z + z; + dir.x = -y * dy - z * dz + 1.0; + dir.y = x * dy - w * dz; + dir.z = x * dz + w * dy; + return dir; + } + + public Vector3d positiveY(Vector3d dir) { + double invNorm = 1.0 / lengthSquared(); + double nx = -x * invNorm; + double ny = -y * invNorm; + double nz = -z * invNorm; + double nw = w * invNorm; + double dx = nx + nx; + double dy = ny + ny; + double dz = nz + nz; + dir.x = nx * dy - nw * dz; + dir.y = -nx * dx - nz * dz + 1.0; + dir.z = ny * dz + nw * dx; + return dir; + } + + public Vector3d normalizedPositiveY(Vector3d dir) { + double dx = x + x; + double dy = y + y; + double dz = z + z; + dir.x = x * dy + w * dz; + dir.y = -x * dx - z * dz + 1.0; + dir.z = y * dz - w * dx; + return dir; + } + + public Vector3d positiveZ(Vector3d dir) { + double invNorm = 1.0 / lengthSquared(); + double nx = -x * invNorm; + double ny = -y * invNorm; + double nz = -z * invNorm; + double nw = w * invNorm; + double dx = nx + nx; + double dy = ny + ny; + double dz = nz + nz; + dir.x = nx * dz + nw * dy; + dir.y = ny * dz - nw * dx; + dir.z = -nx * dx - ny * dy + 1.0; + return dir; + } + + public Vector3d normalizedPositiveZ(Vector3d dir) { + double dx = x + x; + double dy = y + y; + double dz = z + z; + dir.x = x * dz - w * dy; + dir.y = y * dz + w * dx; + dir.z = -x * dx - y * dy + 1.0; + return dir; + } + + /** + * Conjugate this by the given quaternion q by computing q * this * q^-1. + * + * @param q + * the {@link Quaterniondc} to conjugate this by + * @return this + */ + public Quaterniond conjugateBy(Quaterniondc q) { + return conjugateBy(q, this); + } + + /** + * Conjugate this by the given quaternion q by computing q * this * q^-1 + * and store the result into dest. + * + * @param q + * the {@link Quaterniondc} to conjugate this by + * @param dest + * will hold the result + * @return dest + */ + public Quaterniond conjugateBy(Quaterniondc q, Quaterniond dest) { + double invNorm = 1.0 / q.lengthSquared(); + double qix = -q.x() * invNorm, qiy = -q.y() * invNorm, qiz = -q.z() * invNorm, qiw = q.w() * invNorm; + double qpx = Math.fma(q.w(), x, Math.fma(q.x(), w, Math.fma(q.y(), z, -q.z() * y))); + double qpy = Math.fma(q.w(), y, Math.fma(-q.x(), z, Math.fma(q.y(), w, q.z() * x))); + double qpz = Math.fma(q.w(), z, Math.fma(q.x(), y, Math.fma(-q.y(), x, q.z() * w))); + double qpw = Math.fma(q.w(), w, Math.fma(-q.x(), x, Math.fma(-q.y(), y, -q.z() * z))); + return dest.set(Math.fma(qpw, qix, Math.fma(qpx, qiw, Math.fma(qpy, qiz, -qpz * qiy))), + Math.fma(qpw, qiy, Math.fma(-qpx, qiz, Math.fma(qpy, qiw, qpz * qix))), + Math.fma(qpw, qiz, Math.fma(qpx, qiy, Math.fma(-qpy, qix, qpz * qiw))), + Math.fma(qpw, qiw, Math.fma(-qpx, qix, Math.fma(-qpy, qiy, -qpz * qiz)))); + } + + public boolean isFinite() { + return Math.isFinite(x) && Math.isFinite(y) && Math.isFinite(z) && Math.isFinite(w); + } + + public boolean equals(Quaterniondc q, double delta) { + if (this == q) + return true; + if (q == null) + return false; + if (!(q instanceof Quaterniondc)) + return false; + if (!Runtime.equals(x, q.x(), delta)) + return false; + if (!Runtime.equals(y, q.y(), delta)) + return false; + if (!Runtime.equals(z, q.z(), delta)) + return false; + if (!Runtime.equals(w, q.w(), delta)) + return false; + return true; + } + + public boolean equals(double x, double y, double z, double w) { + if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(x)) + return false; + if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(y)) + return false; + if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(z)) + return false; + if (Double.doubleToLongBits(this.w) != Double.doubleToLongBits(w)) + return false; + return true; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/QuaterniondInterpolator.java b/src/main/java/com/jozufozu/flywheel/repack/joml/QuaterniondInterpolator.java new file mode 100644 index 000000000..6329c4540 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/QuaterniondInterpolator.java @@ -0,0 +1,354 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Computes the weighted average of multiple rotations represented as {@link Quaterniond} instances. + *

+ * Instances of this class are not thread-safe. + * + * @author Kai Burjack + */ +public class QuaterniondInterpolator { + + /** + * Performs singular value decomposition on {@link Matrix3d}. + *

+ * This code was adapted from http://www.public.iastate.edu/. + * + * @author Kai Burjack + */ + private static class SvdDecomposition3d { + private final double rv1[]; + private final double w[]; + private final double v[]; + + SvdDecomposition3d() { + this.rv1 = new double[3]; + this.w = new double[3]; + this.v = new double[9]; + } + + private double SIGN(double a, double b) { + return (b) >= 0.0 ? Math.abs(a) : -Math.abs(a); + } + + void svd(double[] a, int maxIterations, Matrix3d destU, Matrix3d destV) { + int flag, i, its, j, jj, k, l = 0, nm = 0; + double c, f, h, s, x, y, z; + double anorm = 0.0, g = 0.0, scale = 0.0; + /* Householder reduction to bidiagonal form */ + for (i = 0; i < 3; i++) { + /* left-hand reduction */ + l = i + 1; + rv1[i] = scale * g; + g = s = scale = 0.0; + for (k = i; k < 3; k++) + scale += Math.abs(a[k + 3 * i]); + if (scale != 0.0) { + for (k = i; k < 3; k++) { + a[k + 3 * i] = (a[k + 3 * i] / scale); + s += (a[k + 3 * i] * a[k + 3 * i]); + } + f = a[i + 3 * i]; + g = -SIGN(Math.sqrt(s), f); + h = f * g - s; + a[i + 3 * i] = f - g; + if (i != 3 - 1) { + for (j = l; j < 3; j++) { + for (s = 0.0, k = i; k < 3; k++) + s += a[k + 3 * i] * a[k + 3 * j]; + f = s / h; + for (k = i; k < 3; k++) + a[k + 3 * j] += f * a[k + 3 * i]; + } + } + for (k = i; k < 3; k++) + a[k + 3 * i] = a[k + 3 * i] * scale; + } + w[i] = (scale * g); + + /* right-hand reduction */ + g = s = scale = 0.0; + if (i < 3 && i != 3 - 1) { + for (k = l; k < 3; k++) + scale += Math.abs(a[i + 3 * k]); + if (scale != 0.0) { + for (k = l; k < 3; k++) { + a[i + 3 * k] = a[i + 3 * k] / scale; + s += a[i + 3 * k] * a[i + 3 * k]; + } + f = a[i + 3 * l]; + g = -SIGN(Math.sqrt(s), f); + h = f * g - s; + a[i + 3 * l] = f - g; + for (k = l; k < 3; k++) + rv1[k] = a[i + 3 * k] / h; + if (i != 3 - 1) { + for (j = l; j < 3; j++) { + for (s = 0.0, k = l; k < 3; k++) + s += a[j + 3 * k] * a[i + 3 * k]; + for (k = l; k < 3; k++) + a[j + 3 * k] += s * rv1[k]; + } + } + for (k = l; k < 3; k++) + a[i + 3 * k] = a[i + 3 * k] * scale; + } + } + anorm = Math.max(anorm, (Math.abs(w[i]) + Math.abs(rv1[i]))); + } + + /* accumulate the right-hand transformation */ + for (i = 3 - 1; i >= 0; i--) { + if (i < 3 - 1) { + if (g != 0.0) { + for (j = l; j < 3; j++) + v[j + 3 * i] = (a[i + 3 * j] / a[i + 3 * l]) / g; + /* double division to avoid underflow */ + for (j = l; j < 3; j++) { + for (s = 0.0, k = l; k < 3; k++) + s += a[i + 3 * k] * v[k + 3 * j]; + for (k = l; k < 3; k++) + v[k + 3 * j] += s * v[k + 3 * i]; + } + } + for (j = l; j < 3; j++) + v[i + 3 * j] = v[j + 3 * i] = 0.0; + } + v[i + 3 * i] = 1.0; + g = rv1[i]; + l = i; + } + + /* accumulate the left-hand transformation */ + for (i = 3 - 1; i >= 0; i--) { + l = i + 1; + g = w[i]; + if (i < 3 - 1) + for (j = l; j < 3; j++) + a[i + 3 * j] = 0.0; + if (g != 0.0) { + g = 1.0 / g; + if (i != 3 - 1) { + for (j = l; j < 3; j++) { + for (s = 0.0, k = l; k < 3; k++) + s += a[k + 3 * i] * a[k + 3 * j]; + f = s / a[i + 3 * i] * g; + for (k = i; k < 3; k++) + a[k + 3 * j] += f * a[k + 3 * i]; + } + } + for (j = i; j < 3; j++) + a[j + 3 * i] = a[j + 3 * i] * g; + } else { + for (j = i; j < 3; j++) + a[j + 3 * i] = 0.0; + } + ++a[i + 3 * i]; + } + + /* diagonalize the bidiagonal form */ + for (k = 3 - 1; k >= 0; k--) { /* loop over singular values */ + for (its = 0; its < maxIterations; its++) { /* loop over allowed iterations */ + flag = 1; + for (l = k; l >= 0; l--) { /* test for splitting */ + nm = l - 1; + if (Math.abs(rv1[l]) + anorm == anorm) { + flag = 0; + break; + } + if (Math.abs(w[nm]) + anorm == anorm) + break; + } + if (flag != 0) { + c = 0.0; + s = 1.0; + for (i = l; i <= k; i++) { + f = s * rv1[i]; + if (Math.abs(f) + anorm != anorm) { + g = w[i]; + h = PYTHAG(f, g); + w[i] = h; + h = 1.0 / h; + c = g * h; + s = (-f * h); + for (j = 0; j < 3; j++) { + y = a[j + 3 * nm]; + z = a[j + 3 * i]; + a[j + 3 * nm] = y * c + z * s; + a[j + 3 * i] = z * c - y * s; + } + } + } + } + z = w[k]; + if (l == k) { /* convergence */ + if (z < 0.0) { /* make singular value nonnegative */ + w[k] = -z; + for (j = 0; j < 3; j++) + v[j + 3 * k] = (-v[j + 3 * k]); + } + break; + } + if (its == maxIterations - 1) { + throw new RuntimeException("No convergence after " + maxIterations + " iterations"); + } + + /* shift from bottom 2 x 2 minor */ + x = w[l]; + nm = k - 1; + y = w[nm]; + g = rv1[nm]; + h = rv1[k]; + f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y); + g = PYTHAG(f, 1.0); + f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x; + + /* next QR transformation */ + c = s = 1.0; + for (j = l; j <= nm; j++) { + i = j + 1; + g = rv1[i]; + y = w[i]; + h = s * g; + g = c * g; + z = PYTHAG(f, h); + rv1[j] = z; + c = f / z; + s = h / z; + f = x * c + g * s; + g = g * c - x * s; + h = y * s; + y = y * c; + for (jj = 0; jj < 3; jj++) { + x = v[jj + 3 * j]; + z = v[jj + 3 * i]; + v[jj + 3 * j] = x * c + z * s; + v[jj + 3 * i] = z * c - x * s; + } + z = PYTHAG(f, h); + w[j] = z; + if (z != 0.0) { + z = 1.0 / z; + c = f * z; + s = h * z; + } + f = (c * g) + (s * y); + x = (c * y) - (s * g); + for (jj = 0; jj < 3; jj++) { + y = a[jj + 3 * j]; + z = a[jj + 3 * i]; + a[jj + 3 * j] = y * c + z * s; + a[jj + 3 * i] = z * c - y * s; + } + } + rv1[l] = 0.0; + rv1[k] = f; + w[k] = x; + } + } + destU.set(a); + destV.set(v); + } + + private static double PYTHAG(double a, double b) { + double at = Math.abs(a), bt = Math.abs(b), ct, result; + if (at > bt) { + ct = bt / at; + result = at * Math.sqrt(1.0 + ct * ct); + } else if (bt > 0.0) { + ct = at / bt; + result = bt * Math.sqrt(1.0 + ct * ct); + } else + result = 0.0; + return (result); + } + } + + private final SvdDecomposition3d svdDecomposition3d = new SvdDecomposition3d(); + private final double[] m = new double[9]; + private final Matrix3d u = new Matrix3d(); + private final Matrix3d v = new Matrix3d(); + + /** + * Compute the weighted average of all of the quaternions given in qs using the specified interpolation factors weights, and store the result in dest. + * + * @param qs + * the quaternions to interpolate over + * @param weights + * the weights of each individual quaternion in qs + * @param maxSvdIterations + * the maximum number of iterations in the Singular Value Decomposition step used by this method + * @param dest + * will hold the result + * @return dest + */ + public Quaterniond computeWeightedAverage(Quaterniond[] qs, double[] weights, int maxSvdIterations, Quaterniond dest) { + double m00 = 0.0, m01 = 0.0, m02 = 0.0; + double m10 = 0.0, m11 = 0.0, m12 = 0.0; + double m20 = 0.0, m21 = 0.0, m22 = 0.0; + // Sum the rotation matrices of qs + for (int i = 0; i < qs.length; i++) { + Quaterniond q = qs[i]; + double dx = q.x + q.x; + double dy = q.y + q.y; + double dz = q.z + q.z; + double q00 = dx * q.x; + double q11 = dy * q.y; + double q22 = dz * q.z; + double q01 = dx * q.y; + double q02 = dx * q.z; + double q03 = dx * q.w; + double q12 = dy * q.z; + double q13 = dy * q.w; + double q23 = dz * q.w; + m00 += weights[i] * (1.0 - q11 - q22); + m01 += weights[i] * (q01 + q23); + m02 += weights[i] * (q02 - q13); + m10 += weights[i] * (q01 - q23); + m11 += weights[i] * (1.0 - q22 - q00); + m12 += weights[i] * (q12 + q03); + m20 += weights[i] * (q02 + q13); + m21 += weights[i] * (q12 - q03); + m22 += weights[i] * (1.0 - q11 - q00); + } + m[0] = m00; + m[1] = m01; + m[2] = m02; + m[3] = m10; + m[4] = m11; + m[5] = m12; + m[6] = m20; + m[7] = m21; + m[8] = m22; + // Compute the Singular Value Decomposition of 'm' + svdDecomposition3d.svd(m, maxSvdIterations, u, v); + // Compute rotation matrix + u.mul(v.transpose()); + // Build quaternion from it + return dest.setFromNormalized(u).normalize(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Quaterniondc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Quaterniondc.java new file mode 100644 index 000000000..b7eeeffc1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Quaterniondc.java @@ -0,0 +1,1966 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.util.*; +/** + * Interface to a read-only view of a quaternion of double-precision floats. + * + * @author Kai Burjack + */ +public interface Quaterniondc { + + /** + * @return the first component of the vector part + */ + double x(); + + /** + * @return the second component of the vector part + */ + double y(); + + /** + * @return the third component of the vector part + */ + double z(); + + /** + * @return the real/scalar part of the quaternion + */ + double w(); + + /** + * Normalize this quaternion and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Quaterniond normalize(Quaterniond dest); + + /** + * Add the quaternion (x, y, z, w) to this quaternion and store the result in dest. + * + * @param x + * the x component of the vector part + * @param y + * the y component of the vector part + * @param z + * the z component of the vector part + * @param w + * the real/scalar component + * @param dest + * will hold the result + * @return dest + */ + Quaterniond add(double x, double y, double z, double w, Quaterniond dest); + + /** + * Add q2 to this quaternion and store the result in dest. + * + * @param q2 + * the quaternion to add to this + * @param dest + * will hold the result + * @return dest + */ + Quaterniond add(Quaterniondc q2, Quaterniond dest); + + /** + * Return the dot product of this {@link Quaterniond} and otherQuat. + * + * @param otherQuat + * the other quaternion + * @return the dot product + */ + double dot(Quaterniondc otherQuat); + + /** + * Return the angle in radians represented by this normalized quaternion rotation. + *

+ * This quaternion must be {@link #normalize(Quaterniond) normalized}. + * + * @return the angle in radians + */ + double angle(); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix3d#set(Quaterniondc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix3d get(Matrix3d dest); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix3f#set(Quaterniondc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix3f get(Matrix3f dest); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix4d#set(Quaterniondc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix4d get(Matrix4d dest); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix4f#set(Quaterniondc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix4f get(Matrix4f dest); + + /** + * Set the given {@link AxisAngle4f} to represent the rotation of + * this quaternion. + * + * @param dest + * the {@link AxisAngle4f} to set + * @return the passed in destination + */ + AxisAngle4f get(AxisAngle4f dest); + + /** + * Set the given {@link AxisAngle4d} to represent the rotation of + * this quaternion. + * + * @param dest + * the {@link AxisAngle4d} to set + * @return the passed in destination + */ + AxisAngle4d get(AxisAngle4d dest); + + /** + * Set the given {@link Quaterniond} to the values of this. + * + * @param dest + * the {@link Quaterniond} to set + * @return the passed in destination + */ + Quaterniond get(Quaterniond dest); + + /** + * Set the given {@link Quaternionf} to the values of this. + * + * @param dest + * the {@link Quaternionf} to set + * @return the passed in destination + */ + Quaternionf get(Quaternionf dest); + + /** + * Multiply this quaternion by q and store the result in dest. + *

+ * If T is this and Q is the given + * quaternion, then the resulting quaternion R is: + *

+ * R = T * Q + *

+ * So, this method uses post-multiplication like the matrix classes, resulting in a + * vector to be transformed by Q first, and then by T. + * + * @param q + * the quaternion to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Quaterniond mul(Quaterniondc q, Quaterniond dest); + + /** + * Multiply this quaternion by the quaternion represented via (qx, qy, qz, qw) and store the result in dest. + *

+ * If T is this and Q is the given + * quaternion, then the resulting quaternion R is: + *

+ * R = T * Q + *

+ * So, this method uses post-multiplication like the matrix classes, resulting in a + * vector to be transformed by Q first, and then by T. + * + * @param qx + * the x component of the quaternion to multiply this by + * @param qy + * the y component of the quaternion to multiply this by + * @param qz + * the z component of the quaternion to multiply this by + * @param qw + * the w component of the quaternion to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Quaterniond mul(double qx, double qy, double qz, double qw, Quaterniond dest); + + /** + * Pre-multiply this quaternion by q and store the result in dest. + *

+ * If T is this and Q is the given quaternion, then the resulting quaternion R is: + *

+ * R = Q * T + *

+ * So, this method uses pre-multiplication, resulting in a vector to be transformed by T first, and then by Q. + * + * @param q + * the quaternion to pre-multiply this by + * @param dest + * will hold the result + * @return dest + */ + Quaterniond premul(Quaterniondc q, Quaterniond dest); + + /** + * Pre-multiply this quaternion by the quaternion represented via (qx, qy, qz, qw) and store the result in dest. + *

+ * If T is this and Q is the given quaternion, then the resulting quaternion R is: + *

+ * R = Q * T + *

+ * So, this method uses pre-multiplication, resulting in a vector to be transformed by T first, and then by Q. + * + * @param qx + * the x component of the quaternion to multiply this by + * @param qy + * the y component of the quaternion to multiply this by + * @param qz + * the z component of the quaternion to multiply this by + * @param qw + * the w component of the quaternion to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Quaterniond premul(double qx, double qy, double qz, double qw, Quaterniond dest); + + /** + * Transform the given vector by this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3d transform(Vector3d vec); + + /** + * Transform the given vector by the inverse of this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3d transformInverse(Vector3d vec); + + /** + * Transform the given vector by this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3d transformUnit(Vector3d vec); + + /** + * Transform the given vector by the inverse of this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3d transformInverseUnit(Vector3d vec); + + /** + * Transform the vector (1, 0, 0) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPositiveX(Vector3d dest); + + /** + * Transform the vector (1, 0, 0) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformPositiveX(Vector4d dest); + + /** + * Transform the vector (1, 0, 0) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnitPositiveX(Vector3d dest); + + /** + * Transform the vector (1, 0, 0) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnitPositiveX(Vector4d dest); + + /** + * Transform the vector (0, 1, 0) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPositiveY(Vector3d dest); + + /** + * Transform the vector (0, 1, 0) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformPositiveY(Vector4d dest); + + /** + * Transform the vector (0, 1, 0) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnitPositiveY(Vector3d dest); + + /** + * Transform the vector (0, 1, 0) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnitPositiveY(Vector4d dest); + + /** + * Transform the vector (0, 0, 1) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPositiveZ(Vector3d dest); + + /** + * Transform the vector (0, 0, 1) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformPositiveZ(Vector4d dest); + + /** + * Transform the vector (0, 0, 1) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnitPositiveZ(Vector3d dest); + + /** + * Transform the vector (0, 0, 1) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnitPositiveZ(Vector4d dest); + + /** + * Transform the given vector by this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4d transform(Vector4d vec); + + /** + * Transform the given vector by the inverse of this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4d transformInverse(Vector4d vec); + + /** + * Transform the given vector by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transform(Vector3dc vec, Vector3d dest); + + /** + * Transform the given vector by the inverse of this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverse(Vector3dc vec, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transform(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverse(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transform(Vector4dc vec, Vector4d dest); + + /** + * Transform the given vector by the inverse of this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformInverse(Vector4dc vec, Vector4d dest); + + /** + * Transform the given vector (x, y, z) by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transform(double x, double y, double z, Vector4d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformInverse(double x, double y, double z, Vector4d dest); + + /** + * Transform the given vector by this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3f transform(Vector3f vec); + + /** + * Transform the given vector by the inverse of this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3f transformInverse(Vector3f vec); + + /** + * Transform the given vector by this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4d transformUnit(Vector4d vec); + + /** + * Transform the given vector by the inverse of this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4d transformInverseUnit(Vector4d vec); + + /** + * Transform the given vector by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnit(Vector3dc vec, Vector3d dest); + + /** + * Transform the given vector by the inverse of this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverseUnit(Vector3dc vec, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnit(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverseUnit(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnit(Vector4dc vec, Vector4d dest); + + /** + * Transform the given vector by the inverse of this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformInverseUnit(Vector4dc vec, Vector4d dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnit(double x, double y, double z, Vector4d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformInverseUnit(double x, double y, double z, Vector4d dest); + + /** + * Transform the given vector by this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3f transformUnit(Vector3f vec); + + /** + * Transform the given vector by the inverse of this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3f transformInverseUnit(Vector3f vec); + + /** + * Transform the vector (1, 0, 0) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPositiveX(Vector3f dest); + + /** + * Transform the vector (1, 0, 0) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformPositiveX(Vector4f dest); + + /** + * Transform the vector (1, 0, 0) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnitPositiveX(Vector3f dest); + + /** + * Transform the vector (1, 0, 0) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnitPositiveX(Vector4f dest); + + /** + * Transform the vector (0, 1, 0) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPositiveY(Vector3f dest); + + /** + * Transform the vector (0, 1, 0) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformPositiveY(Vector4f dest); + + /** + * Transform the vector (0, 1, 0) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnitPositiveY(Vector3f dest); + + /** + * Transform the vector (0, 1, 0) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnitPositiveY(Vector4f dest); + + /** + * Transform the vector (0, 0, 1) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPositiveZ(Vector3f dest); + + /** + * Transform the vector (0, 0, 1) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformPositiveZ(Vector4f dest); + + /** + * Transform the vector (0, 0, 1) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnitPositiveZ(Vector3f dest); + + /** + * Transform the vector (0, 0, 1) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnitPositiveZ(Vector4f dest); + + /** + * Transform the given vector by this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4f transform(Vector4f vec); + + /** + * Transform the given vector by the inverse of this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4f transformInverse(Vector4f vec); + + /** + * Transform the given vector by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transform(Vector3fc vec, Vector3f dest); + + /** + * Transform the given vector by the inverse of this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformInverse(Vector3fc vec, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transform(double x, double y, double z, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformInverse(double x, double y, double z, Vector3f dest); + + /** + * Transform the given vector by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transform(Vector4fc vec, Vector4f dest); + + /** + * Transform the given vector by the inverse of this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformInverse(Vector4fc vec, Vector4f dest); + + /** + * Transform the given vector (x, y, z) by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transform(double x, double y, double z, Vector4f dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformInverse(double x, double y, double z, Vector4f dest); + + /** + * Transform the given vector by this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4f transformUnit(Vector4f vec); + + /** + * Transform the given vector by the inverse of this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4f transformInverseUnit(Vector4f vec); + + /** + * Transform the given vector by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnit(Vector3fc vec, Vector3f dest); + + /** + * Transform the given vector by the inverse of this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformInverseUnit(Vector3fc vec, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnit(double x, double y, double z, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformInverseUnit(double x, double y, double z, Vector3f dest); + + /** + * Transform the given vector by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnit(Vector4fc vec, Vector4f dest); + + /** + * Transform the given vector by the inverse of this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformInverseUnit(Vector4fc vec, Vector4f dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnit(double x, double y, double z, Vector4f dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformInverseUnit(double x, double y, double z, Vector4f dest); + + /** + * Invert this quaternion and store the {@link #normalize(Quaterniond) normalized} result in dest. + *

+ * If this quaternion is already normalized, then {@link #conjugate(Quaterniond)} should be used instead. + * + * @see #conjugate(Quaterniond) + * + * @param dest + * will hold the result + * @return dest + */ + Quaterniond invert(Quaterniond dest); + + /** + * Divide this quaternion by b and store the result in dest. + *

+ * The division expressed using the inverse is performed in the following way: + *

+ * dest = this * b^-1, where b^-1 is the inverse of b. + * + * @param b + * the {@link Quaterniondc} to divide this by + * @param dest + * will hold the result + * @return dest + */ + Quaterniond div(Quaterniondc b, Quaterniond dest); + + /** + * Conjugate this quaternion and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Quaterniond conjugate(Quaterniond dest); + + /** + * Return the square of the length of this quaternion. + * + * @return the length + */ + double lengthSquared(); + + /** + * Interpolate between this {@link #normalize(Quaterniond) unit} quaternion and the specified + * target {@link #normalize(Quaterniond) unit} quaternion using spherical linear interpolation using the specified interpolation factor alpha, + * and store the result in dest. + *

+ * This method resorts to non-spherical linear interpolation when the absolute dot product between this and target is + * below 1E-6. + *

+ * Reference: http://fabiensanglard.net + * + * @param target + * the target of the interpolation, which should be reached with alpha = 1.0 + * @param alpha + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Quaterniond slerp(Quaterniondc target, double alpha, Quaterniond dest); + + /** + * Apply scaling to this quaternion, which results in any vector transformed by the quaternion to change + * its length by the given factor, and store the result in dest. + * + * @param factor + * the scaling factor + * @param dest + * will hold the result + * @return dest + */ + Quaterniond scale(double factor, Quaterniond dest); + + /** + * Integrate the rotation given by the angular velocity (vx, vy, vz) around the x, y and z axis, respectively, + * with respect to the given elapsed time delta dt and add the differentiate rotation to the rotation represented by this quaternion + * and store the result into dest. + *

+ * This method pre-multiplies the rotation given by dt and (vx, vy, vz) by this, so + * the angular velocities are always relative to the local coordinate system of the rotation represented by this quaternion. + *

+ * This method is equivalent to calling: rotateLocal(dt * vx, dt * vy, dt * vz, dest) + *

+ * Reference: http://physicsforgames.blogspot.de/ + * + * @param dt + * the delta time + * @param vx + * the angular velocity around the x axis + * @param vy + * the angular velocity around the y axis + * @param vz + * the angular velocity around the z axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond integrate(double dt, double vx, double vy, double vz, Quaterniond dest); + + /** + * Compute a linear (non-spherical) interpolation of this and the given quaternion q + * and store the result in dest. + *

+ * Reference: http://fabiensanglard.net + * + * @param q + * the other quaternion + * @param factor + * the interpolation factor. It is between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Quaterniond nlerp(Quaterniondc q, double factor, Quaterniond dest); + + /** + * Compute linear (non-spherical) interpolations of this and the given quaternion q + * iteratively and store the result in dest. + *

+ * This method performs a series of small-step nlerp interpolations to avoid doing a costly spherical linear interpolation, like + * {@link #slerp(Quaterniondc, double, Quaterniond) slerp}, + * by subdividing the rotation arc between this and q via non-spherical linear interpolations as long as + * the absolute dot product of this and q is greater than the given dotThreshold parameter. + *

+ * Thanks to @theagentd at http://www.java-gaming.org/ for providing the code. + * + * @param q + * the other quaternion + * @param alpha + * the interpolation factor, between 0.0 and 1.0 + * @param dotThreshold + * the threshold for the dot product of this and q above which this method performs another iteration + * of a small-step linear interpolation + * @param dest + * will hold the result + * @return dest + */ + Quaterniond nlerpIterative(Quaterniondc q, double alpha, double dotThreshold, Quaterniond dest); + + /** + * Apply a rotation to this quaternion that maps the given direction to the positive Z axis, and store the result in dest. + *

+ * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain + * parallel to the plane spanned by the up and dir vectors. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: http://answers.unity3d.com + * + * @see #lookAlong(double, double, double, double, double, double, Quaterniond) + * + * @param dir + * the direction to map to the positive Z axis + * @param up + * the vector which will be mapped to a vector parallel to the plane + * spanned by the given dir and up + * @param dest + * will hold the result + * @return dest + */ + Quaterniond lookAlong(Vector3dc dir, Vector3dc up, Quaterniond dest); + + /** + * Apply a rotation to this quaternion that maps the given direction to the positive Z axis, and store the result in dest. + *

+ * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain + * parallel to the plane spanned by the up and dir vectors. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: http://answers.unity3d.com + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Quaterniond lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, Quaterniond dest); + + /** + * Compute the difference between this and the other quaternion + * and store the result in dest. + *

+ * The difference is the rotation that has to be applied to get from + * this rotation to other. If T is this, Q + * is other and D is the computed difference, then the following equation holds: + *

+ * T * D = Q + *

+ * It is defined as: D = T^-1 * Q, where T^-1 denotes the {@link #invert(Quaterniond) inverse} of T. + * + * @param other + * the other quaternion + * @param dest + * will hold the result + * @return dest + */ + Quaterniond difference(Quaterniondc other, Quaterniond dest); + + /** + * Apply a rotation to this that rotates the fromDir vector to point along toDir and + * store the result in dest. + *

+ * Since there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: stackoverflow.com + * + * @param fromDirX + * the x-coordinate of the direction to rotate into the destination direction + * @param fromDirY + * the y-coordinate of the direction to rotate into the destination direction + * @param fromDirZ + * the z-coordinate of the direction to rotate into the destination direction + * @param toDirX + * the x-coordinate of the direction to rotate to + * @param toDirY + * the y-coordinate of the direction to rotate to + * @param toDirZ + * the z-coordinate of the direction to rotate to + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateTo(double fromDirX, double fromDirY, double fromDirZ, double toDirX, double toDirY, double toDirZ, Quaterniond dest); + + /** + * Apply a rotation to this that rotates the fromDir vector to point along toDir and + * store the result in dest. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateTo(double, double, double, double, double, double, Quaterniond) + * + * @param fromDir + * the starting direction + * @param toDir + * the destination direction + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateTo(Vector3dc fromDir, Vector3dc toDir, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the x axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the x axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateX(double angle, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the y axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the y axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateY(double angle, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the z axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the z axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateZ(double angle, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the local x axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local x axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateLocalX(double angle, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the local y axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local y axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateLocalY(double angle, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the local z axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local z axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateLocalZ(double angle, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles using rotation sequence XYZ and store the result in dest. + *

+ * This method is equivalent to calling: rotateX(angleX, dest).rotateY(angleY).rotateZ(angleZ) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleX + * the angle in radians to rotate about the x axis + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleZ + * the angle in radians to rotate about the z axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateXYZ(double angleX, double angleY, double angleZ, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles, using the rotation sequence ZYX and store the result in dest. + *

+ * This method is equivalent to calling: rotateZ(angleZ, dest).rotateY(angleY).rotateX(angleX) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleZ + * the angle in radians to rotate about the z axis + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleX + * the angle in radians to rotate about the x axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateZYX(double angleZ, double angleY, double angleX, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles, using the rotation sequence YXZ and store the result in dest. + *

+ * This method is equivalent to calling: rotateY(angleY, dest).rotateX(angleX).rotateZ(angleZ) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleX + * the angle in radians to rotate about the x axis + * @param angleZ + * the angle in radians to rotate about the z axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateYXZ(double angleY, double angleX, double angleZ, Quaterniond dest); + + /** + * Get the euler angles in radians in rotation sequence XYZ of this quaternion and store them in the + * provided parameter eulerAngles. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3d#x} field, the angle around Y in the {@link Vector3d#y} + * field and the angle around Z in the {@link Vector3d#z} field of the supplied {@link Vector3d} instance. + * + * @param eulerAngles + * will hold the euler angles in radians + * @return the passed in vector + */ + Vector3d getEulerAnglesXYZ(Vector3d eulerAngles); + + /** + * Get the euler angles in radians in rotation sequence ZYX of this quaternion and store them in the + * provided parameter eulerAngles. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3d#x} field, the angle around Y in the {@link Vector3d#y} + * field and the angle around Z in the {@link Vector3d#z} field of the supplied {@link Vector3d} instance. + * + * @param eulerAngles + * will hold the euler angles in radians + * @return the passed in vector + */ + Vector3d getEulerAnglesZYX(Vector3d eulerAngles); + + /** + * Apply a rotation to this quaternion rotating the given radians about the specified axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the specified axis + * @param axisX + * the x coordinate of the rotation axis + * @param axisY + * the y coordinate of the rotation axis + * @param axisZ + * the z coordinate of the rotation axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateAxis(double angle, double axisX, double axisY, double axisZ, Quaterniond dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the specified axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateAxis(double, double, double, double, Quaterniond) + * + * @param angle + * the angle in radians to rotate about the specified axis + * @param axis + * the rotation axis + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotateAxis(double angle, Vector3dc axis, Quaterniond dest); + + /** + * Obtain the direction of +X before the rotation transformation represented by this quaternion is applied. + *

+ * This method is equivalent to the following code: + *

+     * Quaterniond inv = new Quaterniond(this).invert();
+     * inv.transform(dir.set(1, 0, 0));
+     * 
+ * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3d positiveX(Vector3d dir); + + /** + * Obtain the direction of +X before the rotation transformation represented by this normalized quaternion is applied. + * The quaternion must be {@link #normalize(Quaterniond) normalized} for this method to work. + *

+ * This method is equivalent to the following code: + *

+     * Quaterniond inv = new Quaterniond(this).conjugate();
+     * inv.transform(dir.set(1, 0, 0));
+     * 
+ * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3d normalizedPositiveX(Vector3d dir); + + /** + * Obtain the direction of +Y before the rotation transformation represented by this quaternion is applied. + *

+ * This method is equivalent to the following code: + *

+     * Quaterniond inv = new Quaterniond(this).invert();
+     * inv.transform(dir.set(0, 1, 0));
+     * 
+ * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3d positiveY(Vector3d dir); + + /** + * Obtain the direction of +Y before the rotation transformation represented by this normalized quaternion is applied. + * The quaternion must be {@link #normalize(Quaterniond) normalized} for this method to work. + *

+ * This method is equivalent to the following code: + *

+     * Quaterniond inv = new Quaterniond(this).conjugate();
+     * inv.transform(dir.set(0, 1, 0));
+     * 
+ * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3d normalizedPositiveY(Vector3d dir); + + /** + * Obtain the direction of +Z before the rotation transformation represented by this quaternion is applied. + *

+ * This method is equivalent to the following code: + *

+     * Quaterniond inv = new Quaterniond(this).invert();
+     * inv.transform(dir.set(0, 0, 1));
+     * 
+ * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3d positiveZ(Vector3d dir); + + /** + * Obtain the direction of +Z before the rotation transformation represented by this normalized quaternion is applied. + * The quaternion must be {@link #normalize(Quaterniond) normalized} for this method to work. + *

+ * This method is equivalent to the following code: + *

+     * Quaterniond inv = new Quaterniond(this).conjugate();
+     * inv.transform(dir.set(0, 0, 1));
+     * 
+ * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3d normalizedPositiveZ(Vector3d dir); + + /** + * Conjugate this by the given quaternion q by computing q * this * q^-1 + * and store the result into dest. + * + * @param q + * the {@link Quaterniondc} to conjugate this by + * @param dest + * will hold the result + * @return dest + */ + Quaterniond conjugateBy(Quaterniondc q, Quaterniond dest); + + /** + * Determine whether all components are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + Compare the quaternion components of this quaternion with the given quaternion using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param q + * the other quaternion + * @param delta + * the allowed maximum difference + * @return true whether all of the quaternion components are equal; false otherwise + */ + boolean equals(Quaterniondc q, double delta); + + /** + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @param z + * the z component to compare to + * @param w + * the w component to compare to + * @return true if all the quaternion components are equal + */ + boolean equals(double x, double y, double z, double w); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Quaternionf.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Quaternionf.java new file mode 100644 index 000000000..43e158686 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Quaternionf.java @@ -0,0 +1,3066 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Quaternion of 4 single-precision floats which can represent rotation and uniform scaling. + * + * @author Richard Greenlees + * @author Kai Burjack + */ +public class Quaternionf implements Externalizable, Cloneable, Quaternionfc { + + private static final long serialVersionUID = 1L; + + /** + * The first component of the vector part. + */ + public float x; + /** + * The second component of the vector part. + */ + public float y; + /** + * The third component of the vector part. + */ + public float z; + /** + * The real/scalar part of the quaternion. + */ + public float w; + + /** + * Create a new {@link Quaternionf} and initialize it with (x=0, y=0, z=0, w=1), + * where (x, y, z) is the vector part of the quaternion and w is the real/scalar part. + */ + public Quaternionf() { + this.w = 1.0f; + } + + /** + * Create a new {@link Quaternionf} and initialize its components to the given values. + * + * @param x + * the first component of the imaginary part + * @param y + * the second component of the imaginary part + * @param z + * the third component of the imaginary part + * @param w + * the real part + */ + public Quaternionf(double x, double y, double z, double w) { + this.x = (float) x; + this.y = (float) y; + this.z = (float) z; + this.w = (float) w; + } + + /** + * Create a new {@link Quaternionf} and initialize its components to the given values. + * + * @param x + * the first component of the imaginary part + * @param y + * the second component of the imaginary part + * @param z + * the third component of the imaginary part + * @param w + * the real part + */ + public Quaternionf(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Quaternionf} and initialize its components to the same values as the given {@link Quaternionfc}. + * + * @param source + * the {@link Quaternionfc} to take the component values from + */ + public Quaternionf(Quaternionfc source) { + set(source); + } + + /** + * Create a new {@link Quaternionf} and initialize its components to the same values as the given {@link Quaterniondc}. + * + * @param source + * the {@link Quaterniondc} to take the component values from + */ + public Quaternionf(Quaterniondc source) { + set(source); + } + + /** + * Create a new {@link Quaternionf} which represents the rotation of the given {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + */ + public Quaternionf(AxisAngle4f axisAngle) { + float sin = Math.sin(axisAngle.angle * 0.5f); + float cos = Math.cosFromSin(sin, axisAngle.angle * 0.5f); + x = axisAngle.x * sin; + y = axisAngle.y * sin; + z = axisAngle.z * sin; + w = cos; + } + + /** + * Create a new {@link Quaterniond} which represents the rotation of the given {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + */ + public Quaternionf(AxisAngle4d axisAngle) { + double sin = Math.sin(axisAngle.angle * 0.5f); + double cos = Math.cosFromSin(sin, axisAngle.angle * 0.5f); + x = (float) (axisAngle.x * sin); + y = (float) (axisAngle.y * sin); + z = (float) (axisAngle.z * sin); + w = (float) cos; + } + + /** + * @return the first component of the vector part + */ + public float x() { + return this.x; + } + + /** + * @return the second component of the vector part + */ + public float y() { + return this.y; + } + + /** + * @return the third component of the vector part + */ + public float z() { + return this.z; + } + + /** + * @return the real/scalar part of the quaternion + */ + public float w() { + return this.w; + } + + /** + * Normalize this quaternion. + * + * @return this + */ + public Quaternionf normalize() { + return normalize(this); + } + + public Quaternionf normalize(Quaternionf dest) { + float invNorm = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + dest.x = x * invNorm; + dest.y = y * invNorm; + dest.z = z * invNorm; + dest.w = w * invNorm; + return dest; + } + + /** + * Add the quaternion (x, y, z, w) to this quaternion. + * + * @param x + * the x component of the vector part + * @param y + * the y component of the vector part + * @param z + * the z component of the vector part + * @param w + * the real/scalar component + * @return this + */ + public Quaternionf add(float x, float y, float z, float w) { + return add(x, y, z, w, this); + } + + public Quaternionf add(float x, float y, float z, float w, Quaternionf dest) { + dest.x = this.x + x; + dest.y = this.y + y; + dest.z = this.z + z; + dest.w = this.w + w; + return dest; + } + + /** + * Add q2 to this quaternion. + * + * @param q2 + * the quaternion to add to this + * @return this + */ + public Quaternionf add(Quaternionfc q2) { + return add(q2, this); + } + + public Quaternionf add(Quaternionfc q2, Quaternionf dest) { + dest.x = x + q2.x(); + dest.y = y + q2.y(); + dest.z = z + q2.z(); + dest.w = w + q2.w(); + return dest; + } + + /** + * Return the dot of this quaternion and otherQuat. + * + * @param otherQuat + * the other quaternion + * @return the dot product + */ + public float dot(Quaternionf otherQuat) { + return this.x * otherQuat.x + this.y * otherQuat.y + this.z * otherQuat.z + this.w * otherQuat.w; + } + + public float angle() { + return (float) (2.0 * Math.safeAcos(w)); + } + + public Matrix3f get(Matrix3f dest) { + return dest.set(this); + } + + public Matrix3d get(Matrix3d dest) { + return dest.set(this); + } + + public Matrix4f get(Matrix4f dest) { + return dest.set(this); + } + + public Matrix4d get(Matrix4d dest) { + return dest.set(this); + } + + public Matrix4x3f get(Matrix4x3f dest) { + return dest.set(this); + } + + public Matrix4x3d get(Matrix4x3d dest) { + return dest.set(this); + } + + public AxisAngle4f get(AxisAngle4f dest) { + float x = this.x; + float y = this.y; + float z = this.z; + float w = this.w; + if (w > 1.0f) { + float invNorm = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + x *= invNorm; + y *= invNorm; + z *= invNorm; + w *= invNorm; + } + dest.angle = (float) (2.0f * Math.acos(w)); + float s = Math.sqrt(1.0f - w * w); + if (s < 0.001f) { + dest.x = x; + dest.y = y; + dest.z = z; + } else { + s = 1.0f / s; + dest.x = x * s; + dest.y = y * s; + dest.z = z * s; + } + return dest; + } + + public AxisAngle4d get(AxisAngle4d dest) { + float x = this.x; + float y = this.y; + float z = this.z; + float w = this.w; + if (w > 1.0f) { + float invNorm = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + x *= invNorm; + y *= invNorm; + z *= invNorm; + w *= invNorm; + } + dest.angle = (float) (2.0f * Math.acos(w)); + float s = Math.sqrt(1.0f - w * w); + if (s < 0.001f) { + dest.x = x; + dest.y = y; + dest.z = z; + } else { + s = 1.0f / s; + dest.x = x * s; + dest.y = y * s; + dest.z = z * s; + } + return dest; + } + + public Quaterniond get(Quaterniond dest) { + return dest.set(this); + } + + /** + * Set the given {@link Quaternionf} to the values of this. + * + * @see #set(Quaternionfc) + * + * @param dest + * the {@link Quaternionf} to set + * @return the passed in destination + */ + public Quaternionf get(Quaternionf dest) { + return dest.set(this); + } + + public ByteBuffer getAsMatrix3f(ByteBuffer dest) { + MemUtil.INSTANCE.putMatrix3f(this, dest.position(), dest); + return dest; + } + + public FloatBuffer getAsMatrix3f(FloatBuffer dest) { + MemUtil.INSTANCE.putMatrix3f(this, dest.position(), dest); + return dest; + } + + public ByteBuffer getAsMatrix4f(ByteBuffer dest) { + MemUtil.INSTANCE.putMatrix4f(this, dest.position(), dest); + return dest; + } + + public FloatBuffer getAsMatrix4f(FloatBuffer dest) { + MemUtil.INSTANCE.putMatrix4f(this, dest.position(), dest); + return dest; + } + + public ByteBuffer getAsMatrix4x3f(ByteBuffer dest) { + MemUtil.INSTANCE.putMatrix4x3f(this, dest.position(), dest); + return dest; + } + + public FloatBuffer getAsMatrix4x3f(FloatBuffer dest) { + MemUtil.INSTANCE.putMatrix4x3f(this, dest.position(), dest); + return dest; + } + + /** + * Set this quaternion to the given values. + * + * @param x + * the new value of x + * @param y + * the new value of y + * @param z + * the new value of z + * @param w + * the new value of w + * @return this + */ + public Quaternionf set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + /** + * Set this quaternion to be a copy of q. + * + * @param q + * the {@link Quaternionfc} to copy + * @return this + */ + public Quaternionf set(Quaternionfc q) { + this.x = q.x(); + this.y = q.y(); + this.z = q.z(); + this.w = q.w(); + return this; + } + + /** + * Set this quaternion to be a copy of q. + * + * @param q + * the {@link Quaterniondc} to copy + * @return this + */ + public Quaternionf set(Quaterniondc q) { + this.x = (float) q.x(); + this.y = (float) q.y(); + this.z = (float) q.z(); + this.w = (float) q.w(); + return this; + } + + /** + * Set this quaternion to a rotation equivalent to the given {@link AxisAngle4f}. + * + * @param axisAngle + * the {@link AxisAngle4f} + * @return this + */ + public Quaternionf set(AxisAngle4f axisAngle) { + return setAngleAxis(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this quaternion to a rotation equivalent to the given {@link AxisAngle4d}. + * + * @param axisAngle + * the {@link AxisAngle4d} + * @return this + */ + public Quaternionf set(AxisAngle4d axisAngle) { + return setAngleAxis(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this quaternion to a rotation equivalent to the supplied axis and + * angle (in radians). + *

+ * This method assumes that the given rotation axis (x, y, z) is already normalized + * + * @param angle + * the angle in radians + * @param x + * the x-component of the normalized rotation axis + * @param y + * the y-component of the normalized rotation axis + * @param z + * the z-component of the normalized rotation axis + * @return this + */ + public Quaternionf setAngleAxis(float angle, float x, float y, float z) { + float s = Math.sin(angle * 0.5f); + this.x = x * s; + this.y = y * s; + this.z = z * s; + this.w = Math.cosFromSin(s, angle * 0.5f); + return this; + } + + /** + * Set this quaternion to a rotation equivalent to the supplied axis and + * angle (in radians). + *

+ * This method assumes that the given rotation axis (x, y, z) is already normalized + * + * @param angle + * the angle in radians + * @param x + * the x-component of the normalized rotation axis + * @param y + * the y-component of the normalized rotation axis + * @param z + * the z-component of the normalized rotation axis + * @return this + */ + public Quaternionf setAngleAxis(double angle, double x, double y, double z) { + double s = Math.sin(angle * 0.5f); + this.x = (float) (x * s); + this.y = (float) (y * s); + this.z = (float) (z * s); + this.w = (float) Math.cosFromSin(s, angle * 0.5f); + return this; + } + + /** + * Set this {@link Quaternionf} to a rotation of the given angle in radians about the supplied + * axis, all of which are specified via the {@link AxisAngle4f}. + * + * @see #rotationAxis(float, float, float, float) + * + * @param axisAngle + * the {@link AxisAngle4f} giving the rotation angle in radians and the axis to rotate about + * @return this + */ + public Quaternionf rotationAxis(AxisAngle4f axisAngle) { + return rotationAxis(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z); + } + + /** + * Set this quaternion to a rotation of the given angle in radians about the supplied axis. + * + * @param angle + * the rotation angle in radians + * @param axisX + * the x-coordinate of the rotation axis + * @param axisY + * the y-coordinate of the rotation axis + * @param axisZ + * the z-coordinate of the rotation axis + * @return this + */ + public Quaternionf rotationAxis(float angle, float axisX, float axisY, float axisZ) { + float hangle = angle / 2.0f; + float sinAngle = Math.sin(hangle); + float invVLength = Math.invsqrt(axisX * axisX + axisY * axisY + axisZ * axisZ); + return set(axisX * invVLength * sinAngle, + axisY * invVLength * sinAngle, + axisZ * invVLength * sinAngle, + Math.cosFromSin(sinAngle, hangle)); + } + + /** + * Set this quaternion to a rotation of the given angle in radians about the supplied axis. + * + * @see #rotationAxis(float, float, float, float) + * + * @param angle + * the rotation angle in radians + * @param axis + * the axis to rotate about + * @return this + */ + public Quaternionf rotationAxis(float angle, Vector3fc axis) { + return rotationAxis(angle, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this quaternion to represent a rotation of the given radians about the x axis. + * + * @param angle + * the angle in radians to rotate about the x axis + * @return this + */ + public Quaternionf rotationX(float angle) { + float sin = Math.sin(angle * 0.5f); + float cos = Math.cosFromSin(sin, angle * 0.5f); + return set(sin, 0, 0, cos); + } + + /** + * Set this quaternion to represent a rotation of the given radians about the y axis. + * + * @param angle + * the angle in radians to rotate about the y axis + * @return this + */ + public Quaternionf rotationY(float angle) { + float sin = Math.sin(angle * 0.5f); + float cos = Math.cosFromSin(sin, angle * 0.5f); + return set(0, sin, 0, cos); + } + + /** + * Set this quaternion to represent a rotation of the given radians about the z axis. + * + * @param angle + * the angle in radians to rotate about the z axis + * @return this + */ + public Quaternionf rotationZ(float angle) { + float sin = Math.sin(angle * 0.5f); + float cos = Math.cosFromSin(sin, angle * 0.5f); + return set(0, 0, sin, cos); + } + + private void setFromUnnormalized(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) { + float nm00 = m00, nm01 = m01, nm02 = m02; + float nm10 = m10, nm11 = m11, nm12 = m12; + float nm20 = m20, nm21 = m21, nm22 = m22; + float lenX = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + float lenY = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + float lenZ = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + setFromNormalized(nm00, nm01, nm02, nm10, nm11, nm12, nm20, nm21, nm22); + } + + private void setFromNormalized(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) { + float t; + float tr = m00 + m11 + m22; + if (tr >= 0.0f) { + t = Math.sqrt(tr + 1.0f); + w = t * 0.5f; + t = 0.5f / t; + x = (m12 - m21) * t; + y = (m20 - m02) * t; + z = (m01 - m10) * t; + } else { + if (m00 >= m11 && m00 >= m22) { + t = Math.sqrt(m00 - (m11 + m22) + 1.0f); + x = t * 0.5f; + t = 0.5f / t; + y = (m10 + m01) * t; + z = (m02 + m20) * t; + w = (m12 - m21) * t; + } else if (m11 > m22) { + t = Math.sqrt(m11 - (m22 + m00) + 1.0f); + y = t * 0.5f; + t = 0.5f / t; + z = (m21 + m12) * t; + x = (m10 + m01) * t; + w = (m20 - m02) * t; + } else { + t = Math.sqrt(m22 - (m00 + m11) + 1.0f); + z = t * 0.5f; + t = 0.5f / t; + x = (m02 + m20) * t; + y = (m21 + m12) * t; + w = (m01 - m10) * t; + } + } + } + + private void setFromUnnormalized(double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22) { + double nm00 = m00, nm01 = m01, nm02 = m02; + double nm10 = m10, nm11 = m11, nm12 = m12; + double nm20 = m20, nm21 = m21, nm22 = m22; + double lenX = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02); + double lenY = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12); + double lenZ = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22); + nm00 *= lenX; nm01 *= lenX; nm02 *= lenX; + nm10 *= lenY; nm11 *= lenY; nm12 *= lenY; + nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ; + setFromNormalized(nm00, nm01, nm02, nm10, nm11, nm12, nm20, nm21, nm22); + } + + private void setFromNormalized(double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22) { + double t; + double tr = m00 + m11 + m22; + if (tr >= 0.0) { + t = Math.sqrt(tr + 1.0); + w = (float) (t * 0.5); + t = 0.5 / t; + x = (float) ((m12 - m21) * t); + y = (float) ((m20 - m02) * t); + z = (float) ((m01 - m10) * t); + } else { + if (m00 >= m11 && m00 >= m22) { + t = Math.sqrt(m00 - (m11 + m22) + 1.0); + x = (float) (t * 0.5); + t = 0.5 / t; + y = (float) ((m10 + m01) * t); + z = (float) ((m02 + m20) * t); + w = (float) ((m12 - m21) * t); + } else if (m11 > m22) { + t = Math.sqrt(m11 - (m22 + m00) + 1.0); + y = (float) (t * 0.5); + t = 0.5 / t; + z = (float) ((m21 + m12) * t); + x = (float) ((m10 + m01) * t); + w = (float) ((m20 - m02) * t); + } else { + t = Math.sqrt(m22 - (m00 + m11) + 1.0); + z = (float) (t * 0.5); + t = 0.5 / t; + x = (float) ((m02 + m20) * t); + y = (float) ((m21 + m12) * t); + w = (float) ((m01 - m10) * t); + } + } + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromUnnormalized(Matrix4fc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromUnnormalized(Matrix4x3fc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromUnnormalized(Matrix4x3dc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromNormalized(Matrix4fc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromNormalized(Matrix4x3fc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromNormalized(Matrix4x3dc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromUnnormalized(Matrix4dc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromNormalized(Matrix4dc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromUnnormalized(Matrix3fc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromNormalized(Matrix3fc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + *

+ * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromUnnormalized(Matrix3dc mat) { + setFromUnnormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the rotational component of the given matrix. + * + * @param mat + * the matrix whose rotational component is used to set this quaternion + * @return this + */ + public Quaternionf setFromNormalized(Matrix3dc mat) { + setFromNormalized(mat.m00(), mat.m01(), mat.m02(), mat.m10(), mat.m11(), mat.m12(), mat.m20(), mat.m21(), mat.m22()); + return this; + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in radians). + * + * @param axis + * the rotation axis + * @param angle + * the angle in radians + * @return this + */ + public Quaternionf fromAxisAngleRad(Vector3fc axis, float angle) { + return fromAxisAngleRad(axis.x(), axis.y(), axis.z(), angle); + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in radians). + * + * @param axisX + * the x component of the rotation axis + * @param axisY + * the y component of the rotation axis + * @param axisZ + * the z component of the rotation axis + * @param angle + * the angle in radians + * @return this + */ + public Quaternionf fromAxisAngleRad(float axisX, float axisY, float axisZ, float angle) { + float hangle = angle / 2.0f; + float sinAngle = Math.sin(hangle); + float vLength = Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ); + x = axisX / vLength * sinAngle; + y = axisY / vLength * sinAngle; + z = axisZ / vLength * sinAngle; + w = Math.cosFromSin(sinAngle, hangle); + return this; + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in degrees). + * + * @param axis + * the rotation axis + * @param angle + * the angle in degrees + * @return this + */ + public Quaternionf fromAxisAngleDeg(Vector3fc axis, float angle) { + return fromAxisAngleRad(axis.x(), axis.y(), axis.z(), Math.toRadians(angle)); + } + + /** + * Set this quaternion to be a representation of the supplied axis and + * angle (in degrees). + * + * @param axisX + * the x component of the rotation axis + * @param axisY + * the y component of the rotation axis + * @param axisZ + * the z component of the rotation axis + * @param angle + * the angle in radians + * @return this + */ + public Quaternionf fromAxisAngleDeg(float axisX, float axisY, float axisZ, float angle) { + return fromAxisAngleRad(axisX, axisY, axisZ, Math.toRadians(angle)); + } + + /** + * Multiply this quaternion by q. + *

+ * If T is this and Q is the given + * quaternion, then the resulting quaternion R is: + *

+ * R = T * Q + *

+ * So, this method uses post-multiplication like the matrix classes, resulting in a + * vector to be transformed by Q first, and then by T. + * + * @param q + * the quaternion to multiply this by + * @return this + */ + public Quaternionf mul(Quaternionfc q) { + return mul(q, this); + } + + public Quaternionf mul(Quaternionfc q, Quaternionf dest) { + return dest.set(Math.fma(w, q.x(), Math.fma(x, q.w(), Math.fma(y, q.z(), -z * q.y()))), + Math.fma(w, q.y(), Math.fma(-x, q.z(), Math.fma(y, q.w(), z * q.x()))), + Math.fma(w, q.z(), Math.fma(x, q.y(), Math.fma(-y, q.x(), z * q.w()))), + Math.fma(w, q.w(), Math.fma(-x, q.x(), Math.fma(-y, q.y(), -z * q.z())))); + } + + /** + * Multiply this quaternion by the quaternion represented via (qx, qy, qz, qw). + *

+ * If T is this and Q is the given + * quaternion, then the resulting quaternion R is: + *

+ * R = T * Q + *

+ * So, this method uses post-multiplication like the matrix classes, resulting in a + * vector to be transformed by Q first, and then by T. + * + * @param qx + * the x component of the quaternion to multiply this by + * @param qy + * the y component of the quaternion to multiply this by + * @param qz + * the z component of the quaternion to multiply this by + * @param qw + * the w component of the quaternion to multiply this by + * @return this + */ + public Quaternionf mul(float qx, float qy, float qz, float qw) { + return mul(qx, qy, qz, qw, this); + } + + public Quaternionf mul(float qx, float qy, float qz, float qw, Quaternionf dest) { + return dest.set(Math.fma(w, qx, Math.fma(x, qw, Math.fma(y, qz, -z * qy))), + Math.fma(w, qy, Math.fma(-x, qz, Math.fma(y, qw, z * qx))), + Math.fma(w, qz, Math.fma(x, qy, Math.fma(-y, qx, z * qw))), + Math.fma(w, qw, Math.fma(-x, qx, Math.fma(-y, qy, -z * qz)))); + } + + /** + * Pre-multiply this quaternion by q. + *

+ * If T is this and Q is the given quaternion, then the resulting quaternion R is: + *

+ * R = Q * T + *

+ * So, this method uses pre-multiplication, resulting in a vector to be transformed by T first, and then by Q. + * + * @param q + * the quaternion to pre-multiply this by + * @return this + */ + public Quaternionf premul(Quaternionfc q) { + return premul(q, this); + } + + public Quaternionf premul(Quaternionfc q, Quaternionf dest) { + return dest.set(Math.fma(q.w(), x, Math.fma(q.x(), w, Math.fma(q.y(), z, -q.z() * y))), + Math.fma(q.w(), y, Math.fma(-q.x(), z, Math.fma(q.y(), w, q.z() * x))), + Math.fma(q.w(), z, Math.fma(q.x(), y, Math.fma(-q.y(), x, q.z() * w))), + Math.fma(q.w(), w, Math.fma(-q.x(), x, Math.fma(-q.y(), y, -q.z() * z)))); + } + + /** + * Pre-multiply this quaternion by the quaternion represented via (qx, qy, qz, qw). + *

+ * If T is this and Q is the given quaternion, then the resulting quaternion R is: + *

+ * R = Q * T + *

+ * So, this method uses pre-multiplication, resulting in a vector to be transformed by T first, and then by Q. + * + * @param qx + * the x component of the quaternion to multiply this by + * @param qy + * the y component of the quaternion to multiply this by + * @param qz + * the z component of the quaternion to multiply this by + * @param qw + * the w component of the quaternion to multiply this by + * @return this + */ + public Quaternionf premul(float qx, float qy, float qz, float qw) { + return premul(qx, qy, qz, qw, this); + } + + public Quaternionf premul(float qx, float qy, float qz, float qw, Quaternionf dest) { + return dest.set(Math.fma(qw, x, Math.fma(qx, w, Math.fma(qy, z, -qz * y))), + Math.fma(qw, y, Math.fma(-qx, z, Math.fma(qy, w, qz * x))), + Math.fma(qw, z, Math.fma(qx, y, Math.fma(-qy, x, qz * w))), + Math.fma(qw, w, Math.fma(-qx, x, Math.fma(-qy, y, -qz * z)))); + } + + public Vector3f transform(Vector3f vec){ + return transform(vec.x, vec.y, vec.z, vec); + } + + public Vector3f transformInverse(Vector3f vec){ + return transformInverse(vec.x, vec.y, vec.z, vec); + } + + public Vector3f transformPositiveX(Vector3f dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float zw = z * w; + float xy = x * y; + float xz = x * z; + float yw = y * w; + dest.x = ww + xx - zz - yy; + dest.y = xy + zw + zw + xy; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector4f transformPositiveX(Vector4f dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float zw = z * w; + float xy = x * y; + float xz = x * z; + float yw = y * w; + dest.x = ww + xx - zz - yy; + dest.y = xy + zw + zw + xy; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector3f transformUnitPositiveX(Vector3f dest) { + float xy = x * y, xz = x * z, yy = y * y; + float yw = y * w, zz = z * z, zw = z * w; + dest.x = 1 - yy - zz - yy - zz; + dest.y = xy + zw + xy + zw; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector4f transformUnitPositiveX(Vector4f dest) { + float yy = y * y, zz = z * z, xy = x * y; + float xz = x * z, yw = y * w, zw = z * w; + dest.x = 1 - yy - yy - zz - zz; + dest.y = xy + zw + xy + zw; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector3f transformPositiveY(Vector3f dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float zw = z * w; + float xy = x * y; + float yz = y * z; + float xw = x * w; + dest.x = -zw + xy - zw + xy; + dest.y = yy - zz + ww - xx; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector4f transformPositiveY(Vector4f dest) { + float ww = w * w, xx = x * x, yy = y * y; + float zz = z * z, zw = z * w, xy = x * y; + float yz = y * z, xw = x * w; + dest.x = -zw + xy - zw + xy; + dest.y = yy - zz + ww - xx; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector4f transformUnitPositiveY(Vector4f dest) { + float xx = x * x, zz = z * z, xy = x * y; + float yz = y * z, xw = x * w, zw = z * w; + dest.x = xy - zw + xy - zw; + dest.y = 1 - xx - xx - zz - zz; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector3f transformUnitPositiveY(Vector3f dest) { + float xx = x * x, zz = z * z, xy = x * y; + float yz = y * z, xw = x * w, zw = z * w; + dest.x = xy - zw + xy - zw; + dest.y = 1 - xx - xx - zz - zz; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector3f transformPositiveZ(Vector3f dest) { + float ww = w * w, xx = x * x, yy = y * y; + float zz = z * z, xz = x * z, yw = y * w; + float yz = y * z, xw = x * w; + dest.x = yw + xz + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = zz - yy - xx + ww; + return dest; + } + + public Vector4f transformPositiveZ(Vector4f dest) { + float ww = w * w, xx = x * x, yy = y * y; + float zz = z * z, xz = x * z, yw = y * w; + float yz = y * z, xw = x * w; + dest.x = yw + xz + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = zz - yy - xx + ww; + return dest; + } + + public Vector4f transformUnitPositiveZ(Vector4f dest) { + float xx = x * x, yy = y * y, xz = x * z; + float yz = y * z, xw = x * w, yw = y * w; + dest.x = xz + yw + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = 1 - xx - xx - yy - yy; + return dest; + } + + public Vector3f transformUnitPositiveZ(Vector3f dest) { + float xx = x * x, yy = y * y, xz = x * z; + float yz = y * z, xw = x * w, yw = y * w; + dest.x = xz + yw + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = 1.0f - xx - xx - yy - yy; + return dest; + } + + public Vector4f transform(Vector4f vec){ + return transform(vec, vec); + } + + public Vector4f transformInverse(Vector4f vec){ + return transformInverse(vec, vec); + } + + public Vector3f transform(Vector3fc vec, Vector3f dest) { + return transform(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3f transformInverse(Vector3fc vec, Vector3f dest) { + return transformInverse(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3f transform(float x, float y, float z, Vector3f dest) { + float xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w; + float xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w; + float zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)), + Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)), + Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector3f transformInverse(float x, float y, float z, Vector3f dest) { + float n = 1.0f / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w))); + float qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n; + float xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw; + float xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw; + float zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)), + Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)), + Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector3f transformUnit(Vector3f vec) { + return transformUnit(vec.x, vec.y, vec.z, vec); + } + + public Vector3f transformInverseUnit(Vector3f vec) { + return transformInverseUnit(vec.x, vec.y, vec.z, vec); + } + + public Vector3f transformUnit(Vector3fc vec, Vector3f dest) { + return transformUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3f transformInverseUnit(Vector3fc vec, Vector3f dest) { + return transformInverseUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3f transformUnit(float x, float y, float z, Vector3f dest) { + float xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + float xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + float yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)), + Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)), + Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector3f transformInverseUnit(float x, float y, float z, Vector3f dest) { + float xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + float xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + float yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)), + Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)), + Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector4f transform(Vector4fc vec, Vector4f dest) { + return transform(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4f transformInverse(Vector4fc vec, Vector4f dest) { + return transformInverse(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4f transform(float x, float y, float z, Vector4f dest) { + float xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w; + float xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w; + float zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)), + Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)), + Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector4f transformInverse(float x, float y, float z, Vector4f dest) { + float n = 1.0f / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w))); + float qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n; + float xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw; + float xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw; + float zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)), + Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)), + Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector3d transform(Vector3d vec){ + return transform(vec.x, vec.y, vec.z, vec); + } + + public Vector3d transformInverse(Vector3d vec){ + return transformInverse(vec.x, vec.y, vec.z, vec); + } + + public Vector4f transformUnit(Vector4f vec) { + return transformUnit(vec.x, vec.y, vec.z, vec); + } + + public Vector4f transformInverseUnit(Vector4f vec) { + return transformInverseUnit(vec.x, vec.y, vec.z, vec); + } + + public Vector4f transformUnit(Vector4fc vec, Vector4f dest) { + return transformUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4f transformInverseUnit(Vector4fc vec, Vector4f dest) { + return transformInverseUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4f transformUnit(float x, float y, float z, Vector4f dest) { + float xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + float xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + float yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)), + Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)), + Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector4f transformInverseUnit(float x, float y, float z, Vector4f dest) { + float xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + float xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + float yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)), + Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)), + Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector3d transformPositiveX(Vector3d dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float zw = z * w; + float xy = x * y; + float xz = x * z; + float yw = y * w; + dest.x = ww + xx - zz - yy; + dest.y = xy + zw + zw + xy; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector4d transformPositiveX(Vector4d dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float zw = z * w; + float xy = x * y; + float xz = x * z; + float yw = y * w; + dest.x = ww + xx - zz - yy; + dest.y = xy + zw + zw + xy; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector3d transformUnitPositiveX(Vector3d dest) { + float yy = y * y; + float zz = z * z; + float xy = x * y; + float xz = x * z; + float yw = y * w; + float zw = z * w; + dest.x = 1 - yy - yy - zz - zz; + dest.y = xy + zw + xy + zw; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector4d transformUnitPositiveX(Vector4d dest) { + float yy = y * y; + float zz = z * z; + float xy = x * y; + float xz = x * z; + float yw = y * w; + float zw = z * w; + dest.x = 1 - yy - yy - zz - zz; + dest.y = xy + zw + xy + zw; + dest.z = xz - yw + xz - yw; + return dest; + } + + public Vector3d transformPositiveY(Vector3d dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float zw = z * w; + float xy = x * y; + float yz = y * z; + float xw = x * w; + dest.x = -zw + xy - zw + xy; + dest.y = yy - zz + ww - xx; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector4d transformPositiveY(Vector4d dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float zw = z * w; + float xy = x * y; + float yz = y * z; + float xw = x * w; + dest.x = -zw + xy - zw + xy; + dest.y = yy - zz + ww - xx; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector4d transformUnitPositiveY(Vector4d dest) { + float xx = x * x; + float zz = z * z; + float xy = x * y; + float yz = y * z; + float xw = x * w; + float zw = z * w; + dest.x = xy - zw + xy - zw; + dest.y = 1 - xx - xx - zz - zz; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector3d transformUnitPositiveY(Vector3d dest) { + float xx = x * x; + float zz = z * z; + float xy = x * y; + float yz = y * z; + float xw = x * w; + float zw = z * w; + dest.x = xy - zw + xy - zw; + dest.y = 1 - xx - xx - zz - zz; + dest.z = yz + yz + xw + xw; + return dest; + } + + public Vector3d transformPositiveZ(Vector3d dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float xz = x * z; + float yw = y * w; + float yz = y * z; + float xw = x * w; + dest.x = yw + xz + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = zz - yy - xx + ww; + return dest; + } + + public Vector4d transformPositiveZ(Vector4d dest) { + float ww = w * w; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float xz = x * z; + float yw = y * w; + float yz = y * z; + float xw = x * w; + dest.x = yw + xz + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = zz - yy - xx + ww; + return dest; + } + + public Vector4d transformUnitPositiveZ(Vector4d dest) { + float xx = x * x; + float yy = y * y; + float xz = x * z; + float yz = y * z; + float xw = x * w; + float yw = y * w; + dest.x = xz + yw + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = 1 - xx - xx - yy - yy; + return dest; + } + + public Vector3d transformUnitPositiveZ(Vector3d dest) { + float xx = x * x; + float yy = y * y; + float xz = x * z; + float yz = y * z; + float xw = x * w; + float yw = y * w; + dest.x = xz + yw + xz + yw; + dest.y = yz + yz - xw - xw; + dest.z = 1 - xx - xx - yy - yy; + return dest; + } + + public Vector4d transform(Vector4d vec){ + return transform(vec, vec); + } + + public Vector4d transformInverse(Vector4d vec){ + return transformInverse(vec, vec); + } + + public Vector3d transform(Vector3dc vec, Vector3d dest) { + return transform(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3d transformInverse(Vector3dc vec, Vector3d dest) { + return transformInverse(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3d transform(float x, float y, float z, Vector3d dest) { + return transform((double) x, (double) y, (double) z, dest); + } + + public Vector3d transformInverse(float x, float y, float z, Vector3d dest) { + return transformInverse((double) x, (double) y, (double) z, dest); + } + + public Vector3d transform(double x, double y, double z, Vector3d dest) { + float xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w; + float xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w; + float zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)), + Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)), + Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector3d transformInverse(double x, double y, double z, Vector3d dest) { + float n = 1.0f / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w))); + float qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n; + float xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw; + float xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw; + float zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)), + Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)), + Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector4d transform(Vector4dc vec, Vector4d dest) { + return transform(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4d transformInverse(Vector4dc vec, Vector4d dest) { + return transformInverse(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4d transform(double x, double y, double z, Vector4d dest) { + float xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w; + float xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w; + float zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)), + Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)), + Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector4d transformInverse(double x, double y, double z, Vector4d dest) { + float n = 1.0f / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w))); + float qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n; + float xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw; + float xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw; + float zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww); + return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)), + Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)), + Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z))); + } + + public Vector4d transformUnit(Vector4d vec){ + return transformUnit(vec, vec); + } + + public Vector4d transformInverseUnit(Vector4d vec){ + return transformInverseUnit(vec, vec); + } + + public Vector3d transformUnit(Vector3dc vec, Vector3d dest) { + return transformUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3d transformInverseUnit(Vector3dc vec, Vector3d dest) { + return transformInverseUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector3d transformUnit(float x, float y, float z, Vector3d dest) { + return transformUnit((double) x, (double) y, (double) z, dest); + } + + public Vector3d transformInverseUnit(float x, float y, float z, Vector3d dest) { + return transformInverseUnit((double) x, (double) y, (double) z, dest); + } + + public Vector3d transformUnit(double x, double y, double z, Vector3d dest) { + float xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + float xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + float yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)), + Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)), + Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector3d transformInverseUnit(double x, double y, double z, Vector3d dest) { + float xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + float xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + float yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)), + Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)), + Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector4d transformUnit(Vector4dc vec, Vector4d dest) { + return transformUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4d transformInverseUnit(Vector4dc vec, Vector4d dest) { + return transformInverseUnit(vec.x(), vec.y(), vec.z(), dest); + } + + public Vector4d transformUnit(double x, double y, double z, Vector4d dest) { + float xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + float xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + float yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)), + Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)), + Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Vector4d transformInverseUnit(double x, double y, double z, Vector4d dest) { + float xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z; + float xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z; + float yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w; + return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)), + Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)), + Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z))); + } + + public Quaternionf invert(Quaternionf dest) { + float invNorm = 1.0f / Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + dest.x = -x * invNorm; + dest.y = -y * invNorm; + dest.z = -z * invNorm; + dest.w = w * invNorm; + return dest; + } + + /** + * Invert this quaternion and {@link #normalize() normalize} it. + *

+ * If this quaternion is already normalized, then {@link #conjugate()} should be used instead. + * + * @see #conjugate() + * + * @return this + */ + public Quaternionf invert() { + return invert(this); + } + + public Quaternionf div(Quaternionfc b, Quaternionf dest) { + float invNorm = 1.0f / Math.fma(b.x(), b.x(), Math.fma(b.y(), b.y(), Math.fma(b.z(), b.z(), b.w() * b.w()))); + float x = -b.x() * invNorm; + float y = -b.y() * invNorm; + float z = -b.z() * invNorm; + float w = b.w() * invNorm; + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Divide this quaternion by b. + *

+ * The division expressed using the inverse is performed in the following way: + *

+ * this = this * b^-1, where b^-1 is the inverse of b. + * + * @param b + * the {@link Quaternionf} to divide this by + * @return this + */ + public Quaternionf div(Quaternionfc b) { + return div(b, this); + } + + /** + * Conjugate this quaternion. + * + * @return this + */ + public Quaternionf conjugate() { + return conjugate(this); + } + + public Quaternionf conjugate(Quaternionf dest) { + dest.x = -x; + dest.y = -y; + dest.z = -z; + dest.w = w; + return dest; + } + + /** + * Set this quaternion to the identity. + * + * @return this + */ + public Quaternionf identity() { + x = 0; + y = 0; + z = 0; + w = 1; + return this; + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles using rotation sequence XYZ. + *

+ * This method is equivalent to calling: rotateX(angleX).rotateY(angleY).rotateZ(angleZ) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleX + * the angle in radians to rotate about the x axis + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleZ + * the angle in radians to rotate about the z axis + * @return this + */ + public Quaternionf rotateXYZ(float angleX, float angleY, float angleZ) { + return rotateXYZ(angleX, angleY, angleZ, this); + } + + public Quaternionf rotateXYZ(float angleX, float angleY, float angleZ, Quaternionf dest) { + float sx = Math.sin(angleX * 0.5f); + float cx = Math.cosFromSin(sx, angleX * 0.5f); + float sy = Math.sin(angleY * 0.5f); + float cy = Math.cosFromSin(sy, angleY * 0.5f); + float sz = Math.sin(angleZ * 0.5f); + float cz = Math.cosFromSin(sz, angleZ * 0.5f); + + float cycz = cy * cz; + float sysz = sy * sz; + float sycz = sy * cz; + float cysz = cy * sz; + float w = cx*cycz - sx*sysz; + float x = sx*cycz + cx*sysz; + float y = cx*sycz - sx*cysz; + float z = cx*cysz + sx*sycz; + // right-multiply + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles, using the rotation sequence ZYX. + *

+ * This method is equivalent to calling: rotateZ(angleZ).rotateY(angleY).rotateX(angleX) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleZ + * the angle in radians to rotate about the z axis + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleX + * the angle in radians to rotate about the x axis + * @return this + */ + public Quaternionf rotateZYX(float angleZ, float angleY, float angleX) { + return rotateZYX(angleZ, angleY, angleX, this); + } + + public Quaternionf rotateZYX(float angleZ, float angleY, float angleX, Quaternionf dest) { + float sx = Math.sin(angleX * 0.5f); + float cx = Math.cosFromSin(sx, angleX * 0.5f); + float sy = Math.sin(angleY * 0.5f); + float cy = Math.cosFromSin(sy, angleY * 0.5f); + float sz = Math.sin(angleZ * 0.5f); + float cz = Math.cosFromSin(sz, angleZ * 0.5f); + + float cycz = cy * cz; + float sysz = sy * sz; + float sycz = sy * cz; + float cysz = cy * sz; + float w = cx*cycz + sx*sysz; + float x = sx*cycz - cx*sysz; + float y = cx*sycz + sx*cysz; + float z = cx*cysz - sx*sycz; + // right-multiply + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles, using the rotation sequence YXZ. + *

+ * This method is equivalent to calling: rotateY(angleY).rotateX(angleX).rotateZ(angleZ) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleX + * the angle in radians to rotate about the x axis + * @param angleZ + * the angle in radians to rotate about the z axis + * @return this + */ + public Quaternionf rotateYXZ(float angleY, float angleX, float angleZ) { + return rotateYXZ(angleY, angleX, angleZ, this); + } + + public Quaternionf rotateYXZ(float angleY, float angleX, float angleZ, Quaternionf dest) { + float sx = Math.sin(angleX * 0.5f); + float cx = Math.cosFromSin(sx, angleX * 0.5f); + float sy = Math.sin(angleY * 0.5f); + float cy = Math.cosFromSin(sy, angleY * 0.5f); + float sz = Math.sin(angleZ * 0.5f); + float cz = Math.cosFromSin(sz, angleZ * 0.5f); + + float yx = cy * sx; + float yy = sy * cx; + float yz = sy * sx; + float yw = cy * cx; + float x = yx * cz + yy * sz; + float y = yy * cz - yx * sz; + float z = yw * sz - yz * cz; + float w = yw * cz + yz * sz; + // right-multiply + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + public Vector3f getEulerAnglesXYZ(Vector3f eulerAngles) { + eulerAngles.x = Math.atan2(x * w - y * z, 0.5f - x * x - y * y); + eulerAngles.y = Math.safeAsin(2.0f * (x * z + y * w)); + eulerAngles.z = Math.atan2(z * w - x * y, 0.5f - y * y - z * z); + return eulerAngles; + } + + public Vector3f getEulerAnglesZYX(Vector3f eulerAngles) { + eulerAngles.x = Math.atan2(y * z + w * x, 0.5f - x * x + y * y); + eulerAngles.y = Math.safeAsin(-2.0f * (x * z - w * y)); + eulerAngles.z = Math.atan2(x * y + w * z, 0.5f - y * y - z * z); + return eulerAngles; + } + + public float lengthSquared() { + return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + } + + /** + * Set this quaternion from the supplied euler angles (in radians) with rotation order XYZ. + *

+ * This method is equivalent to calling: rotationX(angleX).rotateY(angleY).rotateZ(angleZ) + *

+ * Reference: this stackexchange answer + * + * @param angleX + * the angle in radians to rotate about x + * @param angleY + * the angle in radians to rotate about y + * @param angleZ + * the angle in radians to rotate about z + * @return this + */ + public Quaternionf rotationXYZ(float angleX, float angleY, float angleZ) { + float sx = Math.sin(angleX * 0.5f); + float cx = Math.cosFromSin(sx, angleX * 0.5f); + float sy = Math.sin(angleY * 0.5f); + float cy = Math.cosFromSin(sy, angleY * 0.5f); + float sz = Math.sin(angleZ * 0.5f); + float cz = Math.cosFromSin(sz, angleZ * 0.5f); + + float cycz = cy * cz; + float sysz = sy * sz; + float sycz = sy * cz; + float cysz = cy * sz; + w = cx*cycz - sx*sysz; + x = sx*cycz + cx*sysz; + y = cx*sycz - sx*cysz; + z = cx*cysz + sx*sycz; + + return this; + } + + /** + * Set this quaternion from the supplied euler angles (in radians) with rotation order ZYX. + *

+ * This method is equivalent to calling: rotationZ(angleZ).rotateY(angleY).rotateX(angleX) + *

+ * Reference: this stackexchange answer + * + * @param angleX + * the angle in radians to rotate about x + * @param angleY + * the angle in radians to rotate about y + * @param angleZ + * the angle in radians to rotate about z + * @return this + */ + public Quaternionf rotationZYX(float angleZ, float angleY, float angleX) { + float sx = Math.sin(angleX * 0.5f); + float cx = Math.cosFromSin(sx, angleX * 0.5f); + float sy = Math.sin(angleY * 0.5f); + float cy = Math.cosFromSin(sy, angleY * 0.5f); + float sz = Math.sin(angleZ * 0.5f); + float cz = Math.cosFromSin(sz, angleZ * 0.5f); + + float cycz = cy * cz; + float sysz = sy * sz; + float sycz = sy * cz; + float cysz = cy * sz; + w = cx*cycz + sx*sysz; + x = sx*cycz - cx*sysz; + y = cx*sycz + sx*cysz; + z = cx*cysz - sx*sycz; + + return this; + } + + /** + * Set this quaternion from the supplied euler angles (in radians) with rotation order YXZ. + *

+ * This method is equivalent to calling: rotationY(angleY).rotateX(angleX).rotateZ(angleZ) + *

+ * Reference: https://en.wikipedia.org + * + * @param angleY + * the angle in radians to rotate about y + * @param angleX + * the angle in radians to rotate about x + * @param angleZ + * the angle in radians to rotate about z + * @return this + */ + public Quaternionf rotationYXZ(float angleY, float angleX, float angleZ) { + float sx = Math.sin(angleX * 0.5f); + float cx = Math.cosFromSin(sx, angleX * 0.5f); + float sy = Math.sin(angleY * 0.5f); + float cy = Math.cosFromSin(sy, angleY * 0.5f); + float sz = Math.sin(angleZ * 0.5f); + float cz = Math.cosFromSin(sz, angleZ * 0.5f); + + float x = cy * sx; + float y = sy * cx; + float z = sy * sx; + float w = cy * cx; + this.x = x * cz + y * sz; + this.y = y * cz - x * sz; + this.z = w * sz - z * cz; + this.w = w * cz + z * sz; + + return this; + } + + /** + * Interpolate between this {@link #normalize() unit} quaternion and the specified + * target {@link #normalize() unit} quaternion using spherical linear interpolation using the specified interpolation factor alpha. + *

+ * This method resorts to non-spherical linear interpolation when the absolute dot product of this and target is + * below 1E-6f. + * + * @param target + * the target of the interpolation, which should be reached with alpha = 1.0 + * @param alpha + * the interpolation factor, within [0..1] + * @return this + */ + public Quaternionf slerp(Quaternionfc target, float alpha) { + return slerp(target, alpha, this); + } + + public Quaternionf slerp(Quaternionfc target, float alpha, Quaternionf dest) { + float cosom = Math.fma(x, target.x(), Math.fma(y, target.y(), Math.fma(z, target.z(), w * target.w()))); + float absCosom = Math.abs(cosom); + float scale0, scale1; + if (1.0f - absCosom > 1E-6f) { + float sinSqr = 1.0f - absCosom * absCosom; + float sinom = Math.invsqrt(sinSqr); + float omega = Math.atan2(sinSqr * sinom, absCosom); + scale0 = (float) (Math.sin((1.0 - alpha) * omega) * sinom); + scale1 = (float) (Math.sin(alpha * omega) * sinom); + } else { + scale0 = 1.0f - alpha; + scale1 = alpha; + } + scale1 = cosom >= 0.0f ? scale1 : -scale1; + dest.x = Math.fma(scale0, x, scale1 * target.x()); + dest.y = Math.fma(scale0, y, scale1 * target.y()); + dest.z = Math.fma(scale0, z, scale1 * target.z()); + dest.w = Math.fma(scale0, w, scale1 * target.w()); + return dest; + } + + /** + * Interpolate between all of the quaternions given in qs via spherical linear interpolation using the specified interpolation factors weights, + * and store the result in dest. + *

+ * This method will interpolate between each two successive quaternions via {@link #slerp(Quaternionfc, float)} using their relative interpolation weights. + *

+ * This method resorts to non-spherical linear interpolation when the absolute dot product of any two interpolated quaternions is below 1E-6f. + *

+ * Reference: http://gamedev.stackexchange.com/ + * + * @param qs + * the quaternions to interpolate over + * @param weights + * the weights of each individual quaternion in qs + * @param dest + * will hold the result + * @return dest + */ + public static Quaternionfc slerp(Quaternionf[] qs, float[] weights, Quaternionf dest) { + dest.set(qs[0]); + float w = weights[0]; + for (int i = 1; i < qs.length; i++) { + float w0 = w; + float w1 = weights[i]; + float rw1 = w1 / (w0 + w1); + w += w1; + dest.slerp(qs[i], rw1); + } + return dest; + } + + /** + * Apply scaling to this quaternion, which results in any vector transformed by this quaternion to change + * its length by the given factor. + * + * @param factor + * the scaling factor + * @return this + */ + public Quaternionf scale(float factor) { + return scale(factor, this); + } + + public Quaternionf scale(float factor, Quaternionf dest) { + float sqrt = Math.sqrt(factor); + dest.x = sqrt * x; + dest.y = sqrt * y; + dest.z = sqrt * z; + dest.w = sqrt * w; + return dest; + } + + /** + * Set this quaternion to represent scaling, which results in a transformed vector to change + * its length by the given factor. + * + * @param factor + * the scaling factor + * @return this + */ + public Quaternionf scaling(float factor) { + float sqrt = Math.sqrt(factor); + this.x = 0.0f; + this.y = 0.0f; + this.z = 0.0f; + this.w = sqrt; + return this; + } + + /** + * Integrate the rotation given by the angular velocity (vx, vy, vz) around the x, y and z axis, respectively, + * with respect to the given elapsed time delta dt and add the differentiate rotation to the rotation represented by this quaternion. + *

+ * This method pre-multiplies the rotation given by dt and (vx, vy, vz) by this, so + * the angular velocities are always relative to the local coordinate system of the rotation represented by this quaternion. + *

+ * This method is equivalent to calling: rotateLocal(dt * vx, dt * vy, dt * vz) + *

+ * Reference: http://physicsforgames.blogspot.de/ + * + * @param dt + * the delta time + * @param vx + * the angular velocity around the x axis + * @param vy + * the angular velocity around the y axis + * @param vz + * the angular velocity around the z axis + * @return this + */ + public Quaternionf integrate(float dt, float vx, float vy, float vz) { + return integrate(dt, vx, vy, vz, this); + } + + public Quaternionf integrate(float dt, float vx, float vy, float vz, Quaternionf dest) { + float thetaX = dt * vx * 0.5f; + float thetaY = dt * vy * 0.5f; + float thetaZ = dt * vz * 0.5f; + float thetaMagSq = thetaX * thetaX + thetaY * thetaY + thetaZ * thetaZ; + float s; + float dqX, dqY, dqZ, dqW; + if (thetaMagSq * thetaMagSq / 24.0f < 1E-8f) { + dqW = 1.0f - thetaMagSq * 0.5f; + s = 1.0f - thetaMagSq / 6.0f; + } else { + float thetaMag = Math.sqrt(thetaMagSq); + float sin = Math.sin(thetaMag); + s = sin / thetaMag; + dqW = Math.cosFromSin(sin, thetaMag); + } + dqX = thetaX * s; + dqY = thetaY * s; + dqZ = thetaZ * s; + /* Pre-multiplication */ + return dest.set(Math.fma(dqW, x, Math.fma(dqX, w, Math.fma(dqY, z, -dqZ * y))), + Math.fma(dqW, y, Math.fma(-dqX, z, Math.fma(dqY, w, dqZ * x))), + Math.fma(dqW, z, Math.fma(dqX, y, Math.fma(-dqY, x, dqZ * w))), + Math.fma(dqW, w, Math.fma(-dqX, x, Math.fma(-dqY, y, -dqZ * z)))); + } + + /** + * Compute a linear (non-spherical) interpolation of this and the given quaternion q + * and store the result in this. + * + * @param q + * the other quaternion + * @param factor + * the interpolation factor. It is between 0.0 and 1.0 + * @return this + */ + public Quaternionf nlerp(Quaternionfc q, float factor) { + return nlerp(q, factor, this); + } + + public Quaternionf nlerp(Quaternionfc q, float factor, Quaternionf dest) { + float cosom = Math.fma(x, q.x(), Math.fma(y, q.y(), Math.fma(z, q.z(), w * q.w()))); + float scale0 = 1.0f - factor; + float scale1 = (cosom >= 0.0f) ? factor : -factor; + dest.x = Math.fma(scale0, x, scale1 * q.x()); + dest.y = Math.fma(scale0, y, scale1 * q.y()); + dest.z = Math.fma(scale0, z, scale1 * q.z()); + dest.w = Math.fma(scale0, w, scale1 * q.w()); + float s = Math.invsqrt(Math.fma(dest.x, dest.x, Math.fma(dest.y, dest.y, Math.fma(dest.z, dest.z, dest.w * dest.w)))); + dest.x *= s; + dest.y *= s; + dest.z *= s; + dest.w *= s; + return dest; + } + + /** + * Interpolate between all of the quaternions given in qs via non-spherical linear interpolation using the + * specified interpolation factors weights, and store the result in dest. + *

+ * This method will interpolate between each two successive quaternions via {@link #nlerp(Quaternionfc, float)} + * using their relative interpolation weights. + *

+ * Reference: http://gamedev.stackexchange.com/ + * + * @param qs + * the quaternions to interpolate over + * @param weights + * the weights of each individual quaternion in qs + * @param dest + * will hold the result + * @return dest + */ + public static Quaternionfc nlerp(Quaternionfc[] qs, float[] weights, Quaternionf dest) { + dest.set(qs[0]); + float w = weights[0]; + for (int i = 1; i < qs.length; i++) { + float w0 = w; + float w1 = weights[i]; + float rw1 = w1 / (w0 + w1); + w += w1; + dest.nlerp(qs[i], rw1); + } + return dest; + } + + public Quaternionf nlerpIterative(Quaternionfc q, float alpha, float dotThreshold, Quaternionf dest) { + float q1x = x, q1y = y, q1z = z, q1w = w; + float q2x = q.x(), q2y = q.y(), q2z = q.z(), q2w = q.w(); + float dot = Math.fma(q1x, q2x, Math.fma(q1y, q2y, Math.fma(q1z, q2z, q1w * q2w))); + float absDot = Math.abs(dot); + if (1.0f - 1E-6f < absDot) { + return dest.set(this); + } + float alphaN = alpha; + while (absDot < dotThreshold) { + float scale0 = 0.5f; + float scale1 = dot >= 0.0f ? 0.5f : -0.5f; + if (alphaN < 0.5f) { + q2x = Math.fma(scale0, q2x, scale1 * q1x); + q2y = Math.fma(scale0, q2y, scale1 * q1y); + q2z = Math.fma(scale0, q2z, scale1 * q1z); + q2w = Math.fma(scale0, q2w, scale1 * q1w); + float s = Math.invsqrt(Math.fma(q2x, q2x, Math.fma(q2y, q2y, Math.fma(q2z, q2z, q2w * q2w)))); + q2x *= s; + q2y *= s; + q2z *= s; + q2w *= s; + alphaN = alphaN + alphaN; + } else { + q1x = Math.fma(scale0, q1x, scale1 * q2x); + q1y = Math.fma(scale0, q1y, scale1 * q2y); + q1z = Math.fma(scale0, q1z, scale1 * q2z); + q1w = Math.fma(scale0, q1w, scale1 * q2w); + float s = Math.invsqrt(Math.fma(q1x, q1x, Math.fma(q1y, q1y, Math.fma(q1z, q1z, q1w * q1w)))); + q1x *= s; + q1y *= s; + q1z *= s; + q1w *= s; + alphaN = alphaN + alphaN - 1.0f; + } + dot = Math.fma(q1x, q2x, Math.fma(q1y, q2y, Math.fma(q1z, q2z, q1w * q2w))); + absDot = Math.abs(dot); + } + float scale0 = 1.0f - alphaN; + float scale1 = dot >= 0.0f ? alphaN : -alphaN; + float resX = Math.fma(scale0, q1x, scale1 * q2x); + float resY = Math.fma(scale0, q1y, scale1 * q2y); + float resZ = Math.fma(scale0, q1z, scale1 * q2z); + float resW = Math.fma(scale0, q1w, scale1 * q2w); + float s = Math.invsqrt(Math.fma(resX, resX, Math.fma(resY, resY, Math.fma(resZ, resZ, resW * resW)))); + dest.x = resX * s; + dest.y = resY * s; + dest.z = resZ * s; + dest.w = resW * s; + return dest; + } + + /** + * Compute linear (non-spherical) interpolations of this and the given quaternion q + * iteratively and store the result in this. + *

+ * This method performs a series of small-step nlerp interpolations to avoid doing a costly spherical linear interpolation, like + * {@link #slerp(Quaternionfc, float, Quaternionf) slerp}, + * by subdividing the rotation arc between this and q via non-spherical linear interpolations as long as + * the absolute dot product of this and q is greater than the given dotThreshold parameter. + *

+ * Thanks to @theagentd at http://www.java-gaming.org/ for providing the code. + * + * @param q + * the other quaternion + * @param alpha + * the interpolation factor, between 0.0 and 1.0 + * @param dotThreshold + * the threshold for the dot product of this and q above which this method performs another iteration + * of a small-step linear interpolation + * @return this + */ + public Quaternionf nlerpIterative(Quaternionfc q, float alpha, float dotThreshold) { + return nlerpIterative(q, alpha, dotThreshold, this); + } + + /** + * Interpolate between all of the quaternions given in qs via iterative non-spherical linear interpolation using the + * specified interpolation factors weights, and store the result in dest. + *

+ * This method will interpolate between each two successive quaternions via {@link #nlerpIterative(Quaternionfc, float, float)} + * using their relative interpolation weights. + *

+ * Reference: http://gamedev.stackexchange.com/ + * + * @param qs + * the quaternions to interpolate over + * @param weights + * the weights of each individual quaternion in qs + * @param dotThreshold + * the threshold for the dot product of each two interpolated quaternions above which {@link #nlerpIterative(Quaternionfc, float, float)} performs another iteration + * of a small-step linear interpolation + * @param dest + * will hold the result + * @return dest + */ + public static Quaternionfc nlerpIterative(Quaternionf[] qs, float[] weights, float dotThreshold, Quaternionf dest) { + dest.set(qs[0]); + float w = weights[0]; + for (int i = 1; i < qs.length; i++) { + float w0 = w; + float w1 = weights[i]; + float rw1 = w1 / (w0 + w1); + w += w1; + dest.nlerpIterative(qs[i], rw1, dotThreshold); + } + return dest; + } + + /** + * Apply a rotation to this quaternion that maps the given direction to the positive Z axis. + *

+ * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain + * parallel to the plane spanned by the up and dir vectors. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: http://answers.unity3d.com + * + * @see #lookAlong(float, float, float, float, float, float, Quaternionf) + * + * @param dir + * the direction to map to the positive Z axis + * @param up + * the vector which will be mapped to a vector parallel to the plane + * spanned by the given dir and up + * @return this + */ + public Quaternionf lookAlong(Vector3fc dir, Vector3fc up) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), this); + } + + public Quaternionf lookAlong(Vector3fc dir, Vector3fc up, Quaternionf dest) { + return lookAlong(dir.x(), dir.y(), dir.z(), up.x(), up.y(), up.z(), dest); + } + + /** + * Apply a rotation to this quaternion that maps the given direction to the positive Z axis. + *

+ * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain + * parallel to the plane spanned by the up and dir vectors. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: http://answers.unity3d.com + * + * @see #lookAlong(float, float, float, float, float, float, Quaternionf) + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @return this + */ + public Quaternionf lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ) { + return lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this); + } + + public Quaternionf lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Quaternionf dest) { + // Normalize direction + float invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); + float dirnX = -dirX * invDirLength; + float dirnY = -dirY * invDirLength; + float dirnZ = -dirZ * invDirLength; + // left = up x dir + float leftX, leftY, leftZ; + leftX = upY * dirnZ - upZ * dirnY; + leftY = upZ * dirnX - upX * dirnZ; + leftZ = upX * dirnY - upY * dirnX; + // normalize left + float invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ); + leftX *= invLeftLength; + leftY *= invLeftLength; + leftZ *= invLeftLength; + // up = direction x left + float upnX = dirnY * leftZ - dirnZ * leftY; + float upnY = dirnZ * leftX - dirnX * leftZ; + float upnZ = dirnX * leftY - dirnY * leftX; + + /* Convert orthonormal basis vectors to quaternion */ + float x, y, z, w; + double t; + double tr = leftX + upnY + dirnZ; + if (tr >= 0.0) { + t = Math.sqrt(tr + 1.0); + w = (float) (t * 0.5); + t = 0.5 / t; + x = (float) ((dirnY - upnZ) * t); + y = (float) ((leftZ - dirnX) * t); + z = (float) ((upnX - leftY) * t); + } else { + if (leftX > upnY && leftX > dirnZ) { + t = Math.sqrt(1.0 + leftX - upnY - dirnZ); + x = (float) (t * 0.5); + t = 0.5 / t; + y = (float) ((leftY + upnX) * t); + z = (float) ((dirnX + leftZ) * t); + w = (float) ((dirnY - upnZ) * t); + } else if (upnY > dirnZ) { + t = Math.sqrt(1.0 + upnY - leftX - dirnZ); + y = (float) (t * 0.5); + t = 0.5 / t; + x = (float) ((leftY + upnX) * t); + z = (float) ((upnZ + dirnY) * t); + w = (float) ((leftZ - dirnX) * t); + } else { + t = Math.sqrt(1.0 + dirnZ - leftX - upnY); + z = (float) (t * 0.5); + t = 0.5 / t; + x = (float) ((dirnX + leftZ) * t); + y = (float) ((upnZ + dirnY) * t); + w = (float) ((upnX - leftY) * t); + } + } + /* Multiply */ + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Set this quaternion to a rotation that rotates the fromDir vector to point along toDir. + *

+ * Since there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * Reference: stackoverflow.com + * + * @param fromDirX + * the x-coordinate of the direction to rotate into the destination direction + * @param fromDirY + * the y-coordinate of the direction to rotate into the destination direction + * @param fromDirZ + * the z-coordinate of the direction to rotate into the destination direction + * @param toDirX + * the x-coordinate of the direction to rotate to + * @param toDirY + * the y-coordinate of the direction to rotate to + * @param toDirZ + * the z-coordinate of the direction to rotate to + * @return this + */ + public Quaternionf rotationTo(float fromDirX, float fromDirY, float fromDirZ, float toDirX, float toDirY, float toDirZ) { + float fn = Math.invsqrt(Math.fma(fromDirX, fromDirX, Math.fma(fromDirY, fromDirY, fromDirZ * fromDirZ))); + float tn = Math.invsqrt(Math.fma(toDirX, toDirX, Math.fma(toDirY, toDirY, toDirZ * toDirZ))); + float fx = fromDirX * fn, fy = fromDirY * fn, fz = fromDirZ * fn; + float tx = toDirX * tn, ty = toDirY * tn, tz = toDirZ * tn; + float dot = fx * tx + fy * ty + fz * tz; + float x, y, z, w; + if (dot < -1.0f + 1E-6f) { + x = fy; + y = -fx; + z = 0.0f; + w = 0.0f; + if (x * x + y * y == 0.0f) { + x = 0.0f; + y = fz; + z = -fy; + w = 0.0f; + } + this.x = x; + this.y = y; + this.z = z; + this.w = 0; + } else { + float sd2 = Math.sqrt((1.0f + dot) * 2.0f); + float isd2 = 1.0f / sd2; + float cx = fy * tz - fz * ty; + float cy = fz * tx - fx * tz; + float cz = fx * ty - fy * tx; + x = cx * isd2; + y = cy * isd2; + z = cz * isd2; + w = sd2 * 0.5f; + float n2 = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + this.x = x * n2; + this.y = y * n2; + this.z = z * n2; + this.w = w * n2; + } + return this; + } + + /** + * Set this quaternion to a rotation that rotates the fromDir vector to point along toDir. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + * + * @see #rotationTo(float, float, float, float, float, float) + * + * @param fromDir + * the starting direction + * @param toDir + * the destination direction + * @return this + */ + public Quaternionf rotationTo(Vector3fc fromDir, Vector3fc toDir) { + return rotationTo(fromDir.x(), fromDir.y(), fromDir.z(), toDir.x(), toDir.y(), toDir.z()); + } + + public Quaternionf rotateTo(float fromDirX, float fromDirY, float fromDirZ, float toDirX, float toDirY, float toDirZ, Quaternionf dest) { + float fn = Math.invsqrt(Math.fma(fromDirX, fromDirX, Math.fma(fromDirY, fromDirY, fromDirZ * fromDirZ))); + float tn = Math.invsqrt(Math.fma(toDirX, toDirX, Math.fma(toDirY, toDirY, toDirZ * toDirZ))); + float fx = fromDirX * fn, fy = fromDirY * fn, fz = fromDirZ * fn; + float tx = toDirX * tn, ty = toDirY * tn, tz = toDirZ * tn; + float dot = fx * tx + fy * ty + fz * tz; + float x, y, z, w; + if (dot < -1.0f + 1E-6f) { + x = fy; + y = -fx; + z = 0.0f; + w = 0.0f; + if (x * x + y * y == 0.0f) { + x = 0.0f; + y = fz; + z = -fy; + w = 0.0f; + } + } else { + float sd2 = Math.sqrt((1.0f + dot) * 2.0f); + float isd2 = 1.0f / sd2; + float cx = fy * tz - fz * ty; + float cy = fz * tx - fx * tz; + float cz = fx * ty - fy * tx; + x = cx * isd2; + y = cy * isd2; + z = cz * isd2; + w = sd2 * 0.5f; + float n2 = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + x *= n2; + y *= n2; + z *= n2; + w *= n2; + } + /* Multiply */ + return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))), + Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))), + Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))), + Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z)))); + } + + /** + * Apply a rotation to this that rotates the fromDir vector to point along toDir. + *

+ * Since there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateTo(float, float, float, float, float, float, Quaternionf) + * + * @param fromDirX + * the x-coordinate of the direction to rotate into the destination direction + * @param fromDirY + * the y-coordinate of the direction to rotate into the destination direction + * @param fromDirZ + * the z-coordinate of the direction to rotate into the destination direction + * @param toDirX + * the x-coordinate of the direction to rotate to + * @param toDirY + * the y-coordinate of the direction to rotate to + * @param toDirZ + * the z-coordinate of the direction to rotate to + * @return this + */ + public Quaternionf rotateTo(float fromDirX, float fromDirY, float fromDirZ, float toDirX, float toDirY, float toDirZ) { + return rotateTo(fromDirX, fromDirY, fromDirZ, toDirX, toDirY, toDirZ, this); + } + + public Quaternionf rotateTo(Vector3fc fromDir, Vector3fc toDir, Quaternionf dest) { + return rotateTo(fromDir.x(), fromDir.y(), fromDir.z(), toDir.x(), toDir.y(), toDir.z(), dest); + } + + /** + * Apply a rotation to this that rotates the fromDir vector to point along toDir. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateTo(float, float, float, float, float, float, Quaternionf) + * + * @param fromDir + * the starting direction + * @param toDir + * the destination direction + * @return this + */ + public Quaternionf rotateTo(Vector3fc fromDir, Vector3fc toDir) { + return rotateTo(fromDir.x(), fromDir.y(), fromDir.z(), toDir.x(), toDir.y(), toDir.z(), this); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the x axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the x axis + * @return this + */ + public Quaternionf rotateX(float angle) { + return rotateX(angle, this); + } + + public Quaternionf rotateX(float angle, Quaternionf dest) { + float sin = Math.sin(angle * 0.5f); + float cos = Math.cosFromSin(sin, angle * 0.5f); + return dest.set(w * sin + x * cos, + y * cos + z * sin, + z * cos - y * sin, + w * cos - x * sin); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the y axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the y axis + * @return this + */ + public Quaternionf rotateY(float angle) { + return rotateY(angle, this); + } + + public Quaternionf rotateY(float angle, Quaternionf dest) { + float sin = Math.sin(angle * 0.5f); + float cos = Math.cosFromSin(sin, angle * 0.5f); + return dest.set(x * cos - z * sin, + w * sin + y * cos, + x * sin + z * cos, + w * cos - y * sin); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the z axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the z axis + * @return this + */ + public Quaternionf rotateZ(float angle) { + return rotateZ(angle, this); + } + + public Quaternionf rotateZ(float angle, Quaternionf dest) { + float sin = Math.sin(angle * 0.5f); + float cos = Math.cosFromSin(sin, angle * 0.5f); + return dest.set(x * cos + y * sin, + y * cos - x * sin, + w * sin + z * cos, + w * cos - z * sin); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the local x axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local x axis + * @return this + */ + public Quaternionf rotateLocalX(float angle) { + return rotateLocalX(angle, this); + } + + public Quaternionf rotateLocalX(float angle, Quaternionf dest) { + float hangle = angle * 0.5f; + float s = Math.sin(hangle); + float c = Math.cosFromSin(s, hangle); + dest.set(c * x + s * w, + c * y - s * z, + c * z + s * y, + c * w - s * x); + return dest; + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the local y axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local y axis + * @return this + */ + public Quaternionf rotateLocalY(float angle) { + return rotateLocalY(angle, this); + } + + public Quaternionf rotateLocalY(float angle, Quaternionf dest) { + float hangle = angle * 0.5f; + float s = Math.sin(hangle); + float c = Math.cosFromSin(s, hangle); + dest.set(c * x + s * z, + c * y + s * w, + c * z - s * x, + c * w - s * y); + return dest; + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the local z axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local z axis + * @return this + */ + public Quaternionf rotateLocalZ(float angle) { + return rotateLocalZ(angle, this); + } + + public Quaternionf rotateLocalZ(float angle, Quaternionf dest) { + float hangle = angle * 0.5f; + float s = Math.sin(hangle); + float c = Math.cosFromSin(s, hangle); + dest.set(c * x - s * y, + c * y + s * x, + c * z + s * w, + c * w - s * z); + return dest; + } + + public Quaternionf rotateAxis(float angle, float axisX, float axisY, float axisZ, Quaternionf dest) { + float hangle = angle / 2.0f; + float sinAngle = Math.sin(hangle); + float invVLength = Math.invsqrt(Math.fma(axisX, axisX, Math.fma(axisY, axisY, axisZ * axisZ))); + float rx = axisX * invVLength * sinAngle; + float ry = axisY * invVLength * sinAngle; + float rz = axisZ * invVLength * sinAngle; + float rw = Math.cosFromSin(sinAngle, hangle); + return dest.set(Math.fma(this.w, rx, Math.fma(this.x, rw, Math.fma(this.y, rz, -this.z * ry))), + Math.fma(this.w, ry, Math.fma(-this.x, rz, Math.fma(this.y, rw, this.z * rx))), + Math.fma(this.w, rz, Math.fma(this.x, ry, Math.fma(-this.y, rx, this.z * rw))), + Math.fma(this.w, rw, Math.fma(-this.x, rx, Math.fma(-this.y, ry, -this.z * rz)))); + } + + public Quaternionf rotateAxis(float angle, Vector3fc axis, Quaternionf dest) { + return rotateAxis(angle, axis.x(), axis.y(), axis.z(), dest); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the specified axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateAxis(float, float, float, float, Quaternionf) + * + * @param angle + * the angle in radians to rotate about the specified axis + * @param axis + * the rotation axis + * @return this + */ + public Quaternionf rotateAxis(float angle, Vector3fc axis) { + return rotateAxis(angle, axis.x(), axis.y(), axis.z(), this); + } + + /** + * Apply a rotation to this quaternion rotating the given radians about the specified axis. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateAxis(float, float, float, float, Quaternionf) + * + * @param angle + * the angle in radians to rotate about the specified axis + * @param axisX + * the x coordinate of the rotation axis + * @param axisY + * the y coordinate of the rotation axis + * @param axisZ + * the z coordinate of the rotation axis + * @return this + */ + public Quaternionf rotateAxis(float angle, float axisX, float axisY, float axisZ) { + return rotateAxis(angle, axisX, axisY, axisZ, this); + } + + /** + * Return a string representation of this quaternion. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this quaternion by formatting the components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the quaternion components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + " " + Runtime.format(z, formatter) + " " + Runtime.format(w, formatter) + ")"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(x); + out.writeFloat(y); + out.writeFloat(z); + out.writeFloat(w); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + x = in.readFloat(); + y = in.readFloat(); + z = in.readFloat(); + w = in.readFloat(); + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(w); + result = prime * result + Float.floatToIntBits(x); + result = prime * result + Float.floatToIntBits(y); + result = prime * result + Float.floatToIntBits(z); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Quaternionf other = (Quaternionf) obj; + if (Float.floatToIntBits(w) != Float.floatToIntBits(other.w)) + return false; + if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) + return false; + if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) + return false; + if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z)) + return false; + return true; + } + + /** + * Compute the difference between this and the other quaternion + * and store the result in this. + *

+ * The difference is the rotation that has to be applied to get from + * this rotation to other. If T is this, Q + * is other and D is the computed difference, then the following equation holds: + *

+ * T * D = Q + *

+ * It is defined as: D = T^-1 * Q, where T^-1 denotes the {@link #invert() inverse} of T. + * + * @param other + * the other quaternion + * @return this + */ + public Quaternionf difference(Quaternionf other) { + return difference(other, this); + } + + public Quaternionf difference(Quaternionfc other, Quaternionf dest) { + float invNorm = 1.0f / lengthSquared(); + float x = -this.x * invNorm; + float y = -this.y * invNorm; + float z = -this.z * invNorm; + float w = this.w * invNorm; + dest.set(Math.fma(w, other.x(), Math.fma(x, other.w(), Math.fma(y, other.z(), -z * other.y()))), + Math.fma(w, other.y(), Math.fma(-x, other.z(), Math.fma(y, other.w(), z * other.x()))), + Math.fma(w, other.z(), Math.fma(x, other.y(), Math.fma(-y, other.x(), z * other.w()))), + Math.fma(w, other.w(), Math.fma(-x, other.x(), Math.fma(-y, other.y(), -z * other.z())))); + return dest; + } + + public Vector3f positiveX(Vector3f dir) { + float invNorm = 1.0f / lengthSquared(); + float nx = -x * invNorm; + float ny = -y * invNorm; + float nz = -z * invNorm; + float nw = w * invNorm; + float dy = ny + ny; + float dz = nz + nz; + dir.x = -ny * dy - nz * dz + 1.0f; + dir.y = nx * dy + nw * dz; + dir.z = nx * dz - nw * dy; + return dir; + } + + public Vector3f normalizedPositiveX(Vector3f dir) { + float dy = y + y; + float dz = z + z; + dir.x = -y * dy - z * dz + 1.0f; + dir.y = x * dy - w * dz; + dir.z = x * dz + w * dy; + return dir; + } + + public Vector3f positiveY(Vector3f dir) { + float invNorm = 1.0f / lengthSquared(); + float nx = -x * invNorm; + float ny = -y * invNorm; + float nz = -z * invNorm; + float nw = w * invNorm; + float dx = nx + nx; + float dy = ny + ny; + float dz = nz + nz; + dir.x = nx * dy - nw * dz; + dir.y = -nx * dx - nz * dz + 1.0f; + dir.z = ny * dz + nw * dx; + return dir; + } + + public Vector3f normalizedPositiveY(Vector3f dir) { + float dx = x + x; + float dy = y + y; + float dz = z + z; + dir.x = x * dy + w * dz; + dir.y = -x * dx - z * dz + 1.0f; + dir.z = y * dz - w * dx; + return dir; + } + + public Vector3f positiveZ(Vector3f dir) { + float invNorm = 1.0f / lengthSquared(); + float nx = -x * invNorm; + float ny = -y * invNorm; + float nz = -z * invNorm; + float nw = w * invNorm; + float dx = nx + nx; + float dy = ny + ny; + float dz = nz + nz; + dir.x = nx * dz + nw * dy; + dir.y = ny * dz - nw * dx; + dir.z = -nx * dx - ny * dy + 1.0f; + return dir; + } + + public Vector3f normalizedPositiveZ(Vector3f dir) { + float dx = x + x; + float dy = y + y; + float dz = z + z; + dir.x = x * dz - w * dy; + dir.y = y * dz + w * dx; + dir.z = -x * dx - y * dy + 1.0f; + return dir; + } + + /** + * Conjugate this by the given quaternion q by computing q * this * q^-1. + * + * @param q + * the {@link Quaternionfc} to conjugate this by + * @return this + */ + public Quaternionf conjugateBy(Quaternionfc q) { + return conjugateBy(q, this); + } + + /** + * Conjugate this by the given quaternion q by computing q * this * q^-1 + * and store the result into dest. + * + * @param q + * the {@link Quaternionfc} to conjugate this by + * @param dest + * will hold the result + * @return dest + */ + public Quaternionf conjugateBy(Quaternionfc q, Quaternionf dest) { + float invNorm = 1.0f / q.lengthSquared(); + float qix = -q.x() * invNorm, qiy = -q.y() * invNorm, qiz = -q.z() * invNorm, qiw = q.w() * invNorm; + float qpx = Math.fma(q.w(), x, Math.fma(q.x(), w, Math.fma(q.y(), z, -q.z() * y))); + float qpy = Math.fma(q.w(), y, Math.fma(-q.x(), z, Math.fma(q.y(), w, q.z() * x))); + float qpz = Math.fma(q.w(), z, Math.fma(q.x(), y, Math.fma(-q.y(), x, q.z() * w))); + float qpw = Math.fma(q.w(), w, Math.fma(-q.x(), x, Math.fma(-q.y(), y, -q.z() * z))); + return dest.set(Math.fma(qpw, qix, Math.fma(qpx, qiw, Math.fma(qpy, qiz, -qpz * qiy))), + Math.fma(qpw, qiy, Math.fma(-qpx, qiz, Math.fma(qpy, qiw, qpz * qix))), + Math.fma(qpw, qiz, Math.fma(qpx, qiy, Math.fma(-qpy, qix, qpz * qiw))), + Math.fma(qpw, qiw, Math.fma(-qpx, qix, Math.fma(-qpy, qiy, -qpz * qiz)))); + } + + public boolean isFinite() { + return Math.isFinite(x) && Math.isFinite(y) && Math.isFinite(z) && Math.isFinite(w); + } + + public boolean equals(Quaternionfc q, float delta) { + if (this == q) + return true; + if (q == null) + return false; + if (!(q instanceof Quaternionfc)) + return false; + if (!Runtime.equals(x, q.x(), delta)) + return false; + if (!Runtime.equals(y, q.y(), delta)) + return false; + if (!Runtime.equals(z, q.z(), delta)) + return false; + if (!Runtime.equals(w, q.w(), delta)) + return false; + return true; + } + + public boolean equals(float x, float y, float z, float w) { + if (Float.floatToIntBits(this.x) != Float.floatToIntBits(x)) + return false; + if (Float.floatToIntBits(this.y) != Float.floatToIntBits(y)) + return false; + if (Float.floatToIntBits(this.z) != Float.floatToIntBits(z)) + return false; + if (Float.floatToIntBits(this.w) != Float.floatToIntBits(w)) + return false; + return true; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/QuaternionfInterpolator.java b/src/main/java/com/jozufozu/flywheel/repack/joml/QuaternionfInterpolator.java new file mode 100644 index 000000000..596e9c26c --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/QuaternionfInterpolator.java @@ -0,0 +1,354 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Computes the weighted average of multiple rotations represented as {@link Quaternionf} instances. + *

+ * Instances of this class are not thread-safe. + * + * @author Kai Burjack + */ +public class QuaternionfInterpolator { + + /** + * Performs singular value decomposition on {@link Matrix3f}. + *

+ * This code was adapted from http://www.public.iastate.edu/. + * + * @author Kai Burjack + */ + private static class SvdDecomposition3f { + private final float rv1[]; + private final float w[]; + private final float v[]; + + SvdDecomposition3f() { + this.rv1 = new float[3]; + this.w = new float[3]; + this.v = new float[9]; + } + + private float SIGN(float a, float b) { + return ((b) >= 0.0 ? Math.abs(a) : -Math.abs(a)); + } + + void svd(float[] a, int maxIterations, Matrix3f destU, Matrix3f destV) { + int flag, i, its, j, jj, k, l = 0, nm = 0; + float c, f, h, s, x, y, z; + float anorm = 0.0f, g = 0.0f, scale = 0.0f; + /* Householder reduction to bidiagonal form */ + for (i = 0; i < 3; i++) { + /* left-hand reduction */ + l = i + 1; + rv1[i] = scale * g; + g = s = scale = 0.0f; + for (k = i; k < 3; k++) + scale += Math.abs(a[k + 3 * i]); + if (scale != 0.0f) { + for (k = i; k < 3; k++) { + a[k + 3 * i] = (a[k + 3 * i] / scale); + s += (a[k + 3 * i] * a[k + 3 * i]); + } + f = a[i + 3 * i]; + g = -SIGN((float) Math.sqrt(s), f); + h = f * g - s; + a[i + 3 * i] = f - g; + if (i != 3 - 1) { + for (j = l; j < 3; j++) { + for (s = 0.0f, k = i; k < 3; k++) + s += a[k + 3 * i] * a[k + 3 * j]; + f = s / h; + for (k = i; k < 3; k++) + a[k + 3 * j] += f * a[k + 3 * i]; + } + } + for (k = i; k < 3; k++) + a[k + 3 * i] = a[k + 3 * i] * scale; + } + w[i] = scale * g; + + /* right-hand reduction */ + g = s = scale = 0.0f; + if (i < 3 && i != 3 - 1) { + for (k = l; k < 3; k++) + scale += Math.abs(a[i + 3 * k]); + if (scale != 0.0f) { + for (k = l; k < 3; k++) { + a[i + 3 * k] = a[i + 3 * k] / scale; + s += a[i + 3 * k] * a[i + 3 * k]; + } + f = a[i + 3 * l]; + g = -SIGN((float) Math.sqrt(s), f); + h = f * g - s; + a[i + 3 * l] = f - g; + for (k = l; k < 3; k++) + rv1[k] = a[i + 3 * k] / h; + if (i != 3 - 1) { + for (j = l; j < 3; j++) { + for (s = 0.0f, k = l; k < 3; k++) + s += a[j + 3 * k] * a[i + 3 * k]; + for (k = l; k < 3; k++) + a[j + 3 * k] += s * rv1[k]; + } + } + for (k = l; k < 3; k++) + a[i + 3 * k] = a[i + 3 * k] * scale; + } + } + anorm = Math.max(anorm, (Math.abs(w[i]) + Math.abs(rv1[i]))); + } + + /* accumulate the right-hand transformation */ + for (i = 3 - 1; i >= 0; i--) { + if (i < 3 - 1) { + if (g != 0.0f) { + for (j = l; j < 3; j++) + v[j + 3 * i] = (a[i + 3 * j] / a[i + 3 * l]) / g; + /* double division to avoid underflow */ + for (j = l; j < 3; j++) { + for (s = 0.0f, k = l; k < 3; k++) + s += a[i + 3 * k] * v[k + 3 * j]; + for (k = l; k < 3; k++) + v[k + 3 * j] += s * v[k + 3 * i]; + } + } + for (j = l; j < 3; j++) + v[i + 3 * j] = v[j + 3 * i] = 0.0f; + } + v[i + 3 * i] = 1.0f; + g = rv1[i]; + l = i; + } + + /* accumulate the left-hand transformation */ + for (i = 3 - 1; i >= 0; i--) { + l = i + 1; + g = w[i]; + if (i < 3 - 1) + for (j = l; j < 3; j++) + a[i + 3 * j] = 0.0f; + if (g != 0.0f) { + g = 1.0f / g; + if (i != 3 - 1) { + for (j = l; j < 3; j++) { + for (s = 0.0f, k = l; k < 3; k++) + s += a[k + 3 * i] * a[k + 3 * j]; + f = s / a[i + 3 * i] * g; + for (k = i; k < 3; k++) + a[k + 3 * j] += f * a[k + 3 * i]; + } + } + for (j = i; j < 3; j++) + a[j + 3 * i] = a[j + 3 * i] * g; + } else { + for (j = i; j < 3; j++) + a[j + 3 * i] = 0.0f; + } + ++a[i + 3 * i]; + } + + /* diagonalize the bidiagonal form */ + for (k = 3 - 1; k >= 0; k--) { /* loop over singular values */ + for (its = 0; its < maxIterations; its++) { /* loop over allowed iterations */ + flag = 1; + for (l = k; l >= 0; l--) { /* test for splitting */ + nm = l - 1; + if (Math.abs(rv1[l]) + anorm == anorm) { + flag = 0; + break; + } + if (Math.abs(w[nm]) + anorm == anorm) + break; + } + if (flag != 0) { + c = 0.0f; + s = 1.0f; + for (i = l; i <= k; i++) { + f = s * rv1[i]; + if (Math.abs(f) + anorm != anorm) { + g = w[i]; + h = PYTHAG(f, g); + w[i] = h; + h = 1.0f / h; + c = g * h; + s = (-f * h); + for (j = 0; j < 3; j++) { + y = a[j + 3 * nm]; + z = a[j + 3 * i]; + a[j + 3 * nm] = y * c + z * s; + a[j + 3 * i] = z * c - y * s; + } + } + } + } + z = w[k]; + if (l == k) { /* convergence */ + if (z < 0.0f) { /* make singular value nonnegative */ + w[k] = -z; + for (j = 0; j < 3; j++) + v[j + 3 * k] = (-v[j + 3 * k]); + } + break; + } + if (its == maxIterations - 1) { + throw new RuntimeException("No convergence after " + maxIterations + " iterations"); + } + + /* shift from bottom 2 x 2 minor */ + x = w[l]; + nm = k - 1; + y = w[nm]; + g = rv1[nm]; + h = rv1[k]; + f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0f * h * y); + g = PYTHAG(f, 1.0f); + f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x; + + /* next QR transformation */ + c = s = 1.0f; + for (j = l; j <= nm; j++) { + i = j + 1; + g = rv1[i]; + y = w[i]; + h = s * g; + g = c * g; + z = PYTHAG(f, h); + rv1[j] = z; + c = f / z; + s = h / z; + f = x * c + g * s; + g = g * c - x * s; + h = y * s; + y = y * c; + for (jj = 0; jj < 3; jj++) { + x = v[jj + 3 * j]; + z = v[jj + 3 * i]; + v[jj + 3 * j] = x * c + z * s; + v[jj + 3 * i] = z * c - x * s; + } + z = PYTHAG(f, h); + w[j] = z; + if (z != 0.0f) { + z = 1.0f / z; + c = f * z; + s = h * z; + } + f = (c * g) + (s * y); + x = (c * y) - (s * g); + for (jj = 0; jj < 3; jj++) { + y = a[jj + 3 * j]; + z = a[jj + 3 * i]; + a[jj + 3 * j] = y * c + z * s; + a[jj + 3 * i] = z * c - y * s; + } + } + rv1[l] = 0.0f; + rv1[k] = f; + w[k] = x; + } + } + destU.set(a); + destV.set(v); + } + + private static float PYTHAG(float a, float b) { + float at = Math.abs(a), bt = Math.abs(b), ct, result; + if (at > bt) { + ct = bt / at; + result = at * (float) Math.sqrt(1.0 + ct * ct); + } else if (bt > 0.0f) { + ct = at / bt; + result = bt * (float) Math.sqrt(1.0 + ct * ct); + } else + result = 0.0f; + return (result); + } + } + + private final SvdDecomposition3f svdDecomposition3f = new SvdDecomposition3f(); + private final float[] m = new float[9]; + private final Matrix3f u = new Matrix3f(); + private final Matrix3f v = new Matrix3f(); + + /** + * Compute the weighted average of all of the quaternions given in qs using the specified interpolation factors weights, and store the result in dest. + * + * @param qs + * the quaternions to interpolate over + * @param weights + * the weights of each individual quaternion in qs + * @param maxSvdIterations + * the maximum number of iterations in the Singular Value Decomposition step used by this method + * @param dest + * will hold the result + * @return dest + */ + public Quaternionf computeWeightedAverage(Quaternionfc[] qs, float[] weights, int maxSvdIterations, Quaternionf dest) { + float m00 = 0.0f, m01 = 0.0f, m02 = 0.0f; + float m10 = 0.0f, m11 = 0.0f, m12 = 0.0f; + float m20 = 0.0f, m21 = 0.0f, m22 = 0.0f; + // Sum the rotation matrices of qs + for (int i = 0; i < qs.length; i++) { + Quaternionfc q = qs[i]; + float dx = q.x() + q.x(); + float dy = q.y() + q.y(); + float dz = q.z() + q.z(); + float q00 = dx * q.x(); + float q11 = dy * q.y(); + float q22 = dz * q.z(); + float q01 = dx * q.y(); + float q02 = dx * q.z(); + float q03 = dx * q.w(); + float q12 = dy * q.z(); + float q13 = dy * q.w(); + float q23 = dz * q.w(); + m00 += weights[i] * (1.0f - q11 - q22); + m01 += weights[i] * (q01 + q23); + m02 += weights[i] * (q02 - q13); + m10 += weights[i] * (q01 - q23); + m11 += weights[i] * (1.0f - q22 - q00); + m12 += weights[i] * (q12 + q03); + m20 += weights[i] * (q02 + q13); + m21 += weights[i] * (q12 - q03); + m22 += weights[i] * (1.0f - q11 - q00); + } + m[0] = m00; + m[1] = m01; + m[2] = m02; + m[3] = m10; + m[4] = m11; + m[5] = m12; + m[6] = m20; + m[7] = m21; + m[8] = m22; + // Compute the Singular Value Decomposition of 'm' + svdDecomposition3f.svd(m, maxSvdIterations, u, v); + // Compute rotation matrix + u.mul(v.transpose()); + // Build quaternion from it + return dest.setFromNormalized(u).normalize(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Quaternionfc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Quaternionfc.java new file mode 100644 index 000000000..495f34dda --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Quaternionfc.java @@ -0,0 +1,2105 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a quaternion of single-precision floats. + * + * @author Kai Burjack + */ +public interface Quaternionfc { + + /** + * @return the first component of the vector part + */ + float x(); + + /** + * @return the second component of the vector part + */ + float y(); + + /** + * @return the third component of the vector part + */ + float z(); + + /** + * @return the real/scalar part of the quaternion + */ + float w(); + + /** + * Normalize this quaternion and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Quaternionf normalize(Quaternionf dest); + + /** + * Add the quaternion (x, y, z, w) to this quaternion and store the result in dest. + * + * @param x + * the x component of the vector part + * @param y + * the y component of the vector part + * @param z + * the z component of the vector part + * @param w + * the real/scalar component + * @param dest + * will hold the result + * @return dest + */ + Quaternionf add(float x, float y, float z, float w, Quaternionf dest); + + /** + * Add q2 to this quaternion and store the result in dest. + * + * @param q2 + * the quaternion to add to this + * @param dest + * will hold the result + * @return dest + */ + Quaternionf add(Quaternionfc q2, Quaternionf dest); + + /** + * Return the angle in radians represented by this normalized quaternion rotation. + *

+ * This quaternion must be {@link #normalize(Quaternionf) normalized}. + * + * @return the angle in radians + */ + float angle(); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix3f#set(Quaternionfc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix3f get(Matrix3f dest); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix3d#set(Quaternionfc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix3d get(Matrix3d dest); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix4f#set(Quaternionfc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix4f get(Matrix4f dest); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix4d#set(Quaternionfc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix4d get(Matrix4d dest); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix4x3f#set(Quaternionfc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix4x3f get(Matrix4x3f dest); + + /** + * Set the given destination matrix to the rotation represented by this. + * + * @see Matrix4x3d#set(Quaternionfc) + * + * @param dest + * the matrix to write the rotation into + * @return the passed in destination + */ + Matrix4x3d get(Matrix4x3d dest); + + /** + * Set the given {@link AxisAngle4f} to represent the rotation of + * this quaternion. + * + * @param dest + * the {@link AxisAngle4f} to set + * @return the passed in destination + */ + AxisAngle4f get(AxisAngle4f dest); + + /** + * Set the given {@link AxisAngle4d} to represent the rotation of + * this quaternion. + * + * @param dest + * the {@link AxisAngle4d} to set + * @return the passed in destination + */ + AxisAngle4d get(AxisAngle4d dest); + + /** + * Set the given {@link Quaterniond} to the values of this. + * + * @see Quaterniond#set(Quaternionfc) + * + * @param dest + * the {@link Quaterniond} to set + * @return the passed in destination + */ + Quaterniond get(Quaterniond dest); + + /** + * Set the given {@link Quaternionf} to the values of this. + * + * @param dest + * the {@link Quaternionf} to set + * @return the passed in destination + */ + Quaternionf get(Quaternionf dest); + + /** + * Store the 3x3 float matrix representation of this quaternion in column-major order into the given {@link ByteBuffer}. + *

+ * This is equivalent to calling: this.get(new Matrix3f()).get(dest) + * + * @param dest + * the destination buffer + * @return dest + */ + ByteBuffer getAsMatrix3f(ByteBuffer dest); + + /** + * Store the 3x3 float matrix representation of this quaternion in column-major order into the given {@link FloatBuffer}. + *

+ * This is equivalent to calling: this.get(new Matrix3f()).get(dest) + * + * @param dest + * the destination buffer + * @return dest + */ + FloatBuffer getAsMatrix3f(FloatBuffer dest); + + /** + * Store the 4x4 float matrix representation of this quaternion in column-major order into the given {@link ByteBuffer}. + *

+ * This is equivalent to calling: this.get(new Matrix4f()).get(dest) + * + * @param dest + * the destination buffer + * @return dest + */ + ByteBuffer getAsMatrix4f(ByteBuffer dest); + + /** + * Store the 4x4 float matrix representation of this quaternion in column-major order into the given {@link FloatBuffer}. + *

+ * This is equivalent to calling: this.get(new Matrix4f()).get(dest) + * + * @param dest + * the destination buffer + * @return dest + */ + FloatBuffer getAsMatrix4f(FloatBuffer dest); + + /** + * Store the 4x3 float matrix representation of this quaternion in column-major order into the given {@link ByteBuffer}. + *

+ * This is equivalent to calling: this.get(new Matrix4x3f()).get(dest) + * + * @param dest + * the destination buffer + * @return dest + */ + ByteBuffer getAsMatrix4x3f(ByteBuffer dest); + + /** + * Store the 4x3 float matrix representation of this quaternion in column-major order into the given {@link FloatBuffer}. + *

+ * This is equivalent to calling: this.get(new Matrix4x3f()).get(dest) + * + * @param dest + * the destination buffer + * @return dest + */ + FloatBuffer getAsMatrix4x3f(FloatBuffer dest); + + /** + * Multiply this quaternion by q and store the result in dest. + *

+ * If T is this and Q is the given + * quaternion, then the resulting quaternion R is: + *

+ * R = T * Q + *

+ * So, this method uses post-multiplication like the matrix classes, resulting in a + * vector to be transformed by Q first, and then by T. + * + * @param q + * the quaternion to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Quaternionf mul(Quaternionfc q, Quaternionf dest); + + /** + * Multiply this quaternion by the quaternion represented via (qx, qy, qz, qw) and store the result in dest. + *

+ * If T is this and Q is the given + * quaternion, then the resulting quaternion R is: + *

+ * R = T * Q + *

+ * So, this method uses post-multiplication like the matrix classes, resulting in a + * vector to be transformed by Q first, and then by T. + * + * @param qx + * the x component of the quaternion to multiply this by + * @param qy + * the y component of the quaternion to multiply this by + * @param qz + * the z component of the quaternion to multiply this by + * @param qw + * the w component of the quaternion to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Quaternionf mul(float qx, float qy, float qz, float qw, Quaternionf dest); + + /** + * Pre-multiply this quaternion by q and store the result in dest. + *

+ * If T is this and Q is the given quaternion, then the resulting quaternion R is: + *

+ * R = Q * T + *

+ * So, this method uses pre-multiplication, resulting in a vector to be transformed by T first, and then by Q. + * + * @param q + * the quaternion to pre-multiply this by + * @param dest + * will hold the result + * @return dest + */ + Quaternionf premul(Quaternionfc q, Quaternionf dest); + + /** + * Pre-multiply this quaternion by the quaternion represented via (qx, qy, qz, qw) and store the result in dest. + *

+ * If T is this and Q is the given quaternion, then the resulting quaternion R is: + *

+ * R = Q * T + *

+ * So, this method uses pre-multiplication, resulting in a vector to be transformed by T first, and then by Q. + * + * @param qx + * the x component of the quaternion to multiply this by + * @param qy + * the y component of the quaternion to multiply this by + * @param qz + * the z component of the quaternion to multiply this by + * @param qw + * the w component of the quaternion to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Quaternionf premul(float qx, float qy, float qz, float qw, Quaternionf dest); + + /** + * Transform the given vector by this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3f transform(Vector3f vec); + + /** + * Transform the given vector by the inverse of this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3f transformInverse(Vector3f vec); + + /** + * Transform the given vector by this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3f transformUnit(Vector3f vec); + + /** + * Transform the vector (1, 0, 0) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPositiveX(Vector3f dest); + + /** + * Transform the vector (1, 0, 0) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformPositiveX(Vector4f dest); + + /** + * Transform the vector (1, 0, 0) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnitPositiveX(Vector3f dest); + + /** + * Transform the vector (1, 0, 0) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnitPositiveX(Vector4f dest); + + /** + * Transform the vector (0, 1, 0) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPositiveY(Vector3f dest); + + /** + * Transform the vector (0, 1, 0) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformPositiveY(Vector4f dest); + + /** + * Transform the vector (0, 1, 0) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnitPositiveY(Vector3f dest); + + /** + * Transform the vector (0, 1, 0) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnitPositiveY(Vector4f dest); + + /** + * Transform the vector (0, 0, 1) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformPositiveZ(Vector3f dest); + + /** + * Transform the vector (0, 0, 1) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformPositiveZ(Vector4f dest); + + /** + * Transform the vector (0, 0, 1) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnitPositiveZ(Vector3f dest); + + /** + * Transform the vector (0, 0, 1) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnitPositiveZ(Vector4f dest); + + /** + * Transform the given vector by this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4f transform(Vector4f vec); + + /** + * Transform the given vector by the inverse of this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4f transformInverse(Vector4f vec); + + /** + * Transform the given vector by this quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transform(Vector3fc vec, Vector3f dest); + + /** + * Transform the given vector by the inverse of quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformInverse(Vector3fc vec, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by this quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transform(float x, float y, float z, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by this quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transform(float x, float y, float z, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of this quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformInverse(float x, float y, float z, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by the inverse of this quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverse(float x, float y, float z, Vector3d dest); + + /** + * Transform the given vector by the inverse of this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3f transformInverseUnit(Vector3f vec); + + /** + * Transform the given vector by this unit quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnit(Vector3fc vec, Vector3f dest); + + /** + * Transform the given vector by the inverse of this unit quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformInverseUnit(Vector3fc vec, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformUnit(float x, float y, float z, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnit(float x, float y, float z, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of this unit quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3f transformInverseUnit(float x, float y, float z, Vector3f dest); + + /** + * Transform the given vector (x, y, z) by the inverse of this unit quaternion + * and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverseUnit(float x, float y, float z, Vector3d dest); + + /** + * Transform the given vector by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transform(Vector4fc vec, Vector4f dest); + + /** + * Transform the given vector by the inverse of this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformInverse(Vector4fc vec, Vector4f dest); + + /** + * Transform the given vector (x, y, z) by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transform(float x, float y, float z, Vector4f dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformInverse(float x, float y, float z, Vector4f dest); + + /** + * Transform the given vector by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnit(Vector4fc vec, Vector4f dest); + + /** + * Transform the given vector by this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4f transformUnit(Vector4f vec); + + /** + * Transform the given vector by the inverse of this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4f transformInverseUnit(Vector4f vec); + + /** + * Transform the given vector by the inverse of this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformInverseUnit(Vector4fc vec, Vector4f dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformUnit(float x, float y, float z, Vector4f dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4f transformInverseUnit(float x, float y, float z, Vector4f dest); + + /** + * Transform the given vector by this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3d transform(Vector3d vec); + + /** + * Transform the given vector by the inverse of this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector3d transformInverse(Vector3d vec); + + /** + * Transform the vector (1, 0, 0) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPositiveX(Vector3d dest); + + /** + * Transform the vector (1, 0, 0) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformPositiveX(Vector4d dest); + + /** + * Transform the vector (1, 0, 0) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnitPositiveX(Vector3d dest); + + /** + * Transform the vector (1, 0, 0) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnitPositiveX(Vector4d dest); + + /** + * Transform the vector (0, 1, 0) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPositiveY(Vector3d dest); + + /** + * Transform the vector (0, 1, 0) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformPositiveY(Vector4d dest); + + /** + * Transform the vector (0, 1, 0) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnitPositiveY(Vector3d dest); + + /** + * Transform the vector (0, 1, 0) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnitPositiveY(Vector4d dest); + + /** + * Transform the vector (0, 0, 1) by this quaternion. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformPositiveZ(Vector3d dest); + + /** + * Transform the vector (0, 0, 1) by this quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformPositiveZ(Vector4d dest); + + /** + * Transform the vector (0, 0, 1) by this unit quaternion. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnitPositiveZ(Vector3d dest); + + /** + * Transform the vector (0, 0, 1) by this unit quaternion. + *

+ * Only the first three components of the given 4D vector are modified. + *

+ * This method is only applicable when this is a unit quaternion. + *

+ * Reference: https://de.mathworks.com/ + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnitPositiveZ(Vector4d dest); + + /** + * Transform the given vector by this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4d transform(Vector4d vec); + + /** + * Transform the given vector by the inverse of this quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4d transformInverse(Vector4d vec); + + /** + * Transform the given vector by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transform(Vector3dc vec, Vector3d dest); + + /** + * Transform the given vector by the inverse of this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverse(Vector3dc vec, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transform(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverse(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transform(Vector4dc vec, Vector4d dest); + + /** + * Transform the given vector by the inverse of this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformInverse(Vector4dc vec, Vector4d dest); + + /** + * Transform the given vector (x, y, z) by this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transform(double x, double y, double z, Vector4d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformInverse(double x, double y, double z, Vector4d dest); + + /** + * Transform the given vector by this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4d transformUnit(Vector4d vec); + + /** + * Transform the given vector by the inverse of this unit quaternion. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and modified. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @return vec + */ + Vector4d transformInverseUnit(Vector4d vec); + + /** + * Transform the given vector by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnit(Vector3dc vec, Vector3d dest); + + /** + * Transform the given vector by the inverse of this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverseUnit(Vector3dc vec, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformUnit(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector3d transformInverseUnit(double x, double y, double z, Vector3d dest); + + /** + * Transform the given vector by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnit(Vector4dc vec, Vector4d dest); + + /** + * Transform the given vector by the inverse of this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * Only the first three components of the given 4D vector are being used and set on the destination. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param vec + * the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformInverseUnit(Vector4dc vec, Vector4d dest); + + /** + * Transform the given vector (x, y, z) by this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformUnit(double x, double y, double z, Vector4d dest); + + /** + * Transform the given vector (x, y, z) by the inverse of + * this unit quaternion and store the result in dest. + *

+ * This will apply the rotation described by this quaternion to the given vector. + *

+ * This method is only applicable when this is a unit quaternion. + * + * @param x + * the x coordinate of the vector to transform + * @param y + * the y coordinate of the vector to transform + * @param z + * the z coordinate of the vector to transform + * @param dest + * will hold the result + * @return dest + */ + Vector4d transformInverseUnit(double x, double y, double z, Vector4d dest); + + /** + * Invert this quaternion and store the {@link #normalize(Quaternionf) normalized} result in dest. + *

+ * If this quaternion is already normalized, then {@link #conjugate(Quaternionf)} should be used instead. + * + * @see #conjugate(Quaternionf) + * + * @param dest + * will hold the result + * @return dest + */ + Quaternionf invert(Quaternionf dest); + + /** + * Divide this quaternion by b and store the result in dest. + *

+ * The division expressed using the inverse is performed in the following way: + *

+ * dest = this * b^-1, where b^-1 is the inverse of b. + * + * @param b + * the {@link Quaternionfc} to divide this by + * @param dest + * will hold the result + * @return dest + */ + Quaternionf div(Quaternionfc b, Quaternionf dest); + + /** + * Conjugate this quaternion and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Quaternionf conjugate(Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles using rotation sequence XYZ and store the result in dest. + *

+ * This method is equivalent to calling: rotateX(angleX, dest).rotateY(angleY).rotateZ(angleZ) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleX + * the angle in radians to rotate about the x axis + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleZ + * the angle in radians to rotate about the z axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateXYZ(float angleX, float angleY, float angleZ, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles, using the rotation sequence ZYX and store the result in dest. + *

+ * This method is equivalent to calling: rotateZ(angleZ, dest).rotateY(angleY).rotateX(angleX) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleZ + * the angle in radians to rotate about the z axis + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleX + * the angle in radians to rotate about the x axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateZYX(float angleZ, float angleY, float angleX, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the cartesian base unit axes, + * called the euler angles, using the rotation sequence YXZ and store the result in dest. + *

+ * This method is equivalent to calling: rotateY(angleY, dest).rotateX(angleX).rotateZ(angleZ) + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angleY + * the angle in radians to rotate about the y axis + * @param angleX + * the angle in radians to rotate about the x axis + * @param angleZ + * the angle in radians to rotate about the z axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateYXZ(float angleY, float angleX, float angleZ, Quaternionf dest); + + /** + * Get the euler angles in radians in rotation sequence XYZ of this quaternion and store them in the + * provided parameter eulerAngles. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + * + * @param eulerAngles + * will hold the euler angles in radians + * @return the passed in vector + */ + Vector3f getEulerAnglesXYZ(Vector3f eulerAngles); + + /** + * Get the euler angles in radians in rotation sequence ZYX of this quaternion and store them in the + * provided parameter eulerAngles. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + * + * @param eulerAngles + * will hold the euler angles in radians + * @return the passed in vector + */ + Vector3f getEulerAnglesZYX(Vector3f eulerAngles); + + /** + * Return the square of the length of this quaternion. + * + * @return the length + */ + float lengthSquared(); + + /** + * Interpolate between this {@link #normalize(Quaternionf) unit} quaternion and the specified + * target {@link #normalize(Quaternionf) unit} quaternion using spherical linear interpolation using the specified interpolation factor alpha, + * and store the result in dest. + *

+ * This method resorts to non-spherical linear interpolation when the absolute dot product of this and target is + * below 1E-6f. + *

+ * Reference: http://fabiensanglard.net + * + * @param target + * the target of the interpolation, which should be reached with alpha = 1.0 + * @param alpha + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Quaternionf slerp(Quaternionfc target, float alpha, Quaternionf dest); + + /** + * Apply scaling to this quaternion, which results in any vector transformed by the quaternion to change + * its length by the given factor, and store the result in dest. + * + * @param factor + * the scaling factor + * @param dest + * will hold the result + * @return dest + */ + Quaternionf scale(float factor, Quaternionf dest); + + /** + * Integrate the rotation given by the angular velocity (vx, vy, vz) around the x, y and z axis, respectively, + * with respect to the given elapsed time delta dt and add the differentiate rotation to the rotation represented by this quaternion + * and store the result into dest. + *

+ * This method pre-multiplies the rotation given by dt and (vx, vy, vz) by this, so + * the angular velocities are always relative to the local coordinate system of the rotation represented by this quaternion. + *

+ * This method is equivalent to calling: rotateLocal(dt * vx, dt * vy, dt * vz, dest) + *

+ * Reference: http://physicsforgames.blogspot.de/ + * + * @param dt + * the delta time + * @param vx + * the angular velocity around the x axis + * @param vy + * the angular velocity around the y axis + * @param vz + * the angular velocity around the z axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf integrate(float dt, float vx, float vy, float vz, Quaternionf dest); + + /** + * Compute a linear (non-spherical) interpolation of this and the given quaternion q + * and store the result in dest. + *

+ * Reference: http://fabiensanglard.net + * + * @param q + * the other quaternion + * @param factor + * the interpolation factor. It is between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Quaternionf nlerp(Quaternionfc q, float factor, Quaternionf dest); + + /** + * Compute linear (non-spherical) interpolations of this and the given quaternion q + * iteratively and store the result in dest. + *

+ * This method performs a series of small-step nlerp interpolations to avoid doing a costly spherical linear interpolation, like + * {@link #slerp(Quaternionfc, float, Quaternionf) slerp}, + * by subdividing the rotation arc between this and q via non-spherical linear interpolations as long as + * the absolute dot product of this and q is greater than the given dotThreshold parameter. + *

+ * Thanks to @theagentd at http://www.java-gaming.org/ for providing the code. + * + * @param q + * the other quaternion + * @param alpha + * the interpolation factor, between 0.0 and 1.0 + * @param dotThreshold + * the threshold for the dot product of this and q above which this method performs another iteration + * of a small-step linear interpolation + * @param dest + * will hold the result + * @return dest + */ + Quaternionf nlerpIterative(Quaternionfc q, float alpha, float dotThreshold, Quaternionf dest); + + /** + * Apply a rotation to this quaternion that maps the given direction to the positive Z axis, and store the result in dest. + *

+ * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain + * parallel to the plane spanned by the up and dir vectors. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: http://answers.unity3d.com + * + * @see #lookAlong(float, float, float, float, float, float, Quaternionf) + * + * @param dir + * the direction to map to the positive Z axis + * @param up + * the vector which will be mapped to a vector parallel to the plane + * spanned by the given dir and up + * @param dest + * will hold the result + * @return dest + */ + Quaternionf lookAlong(Vector3fc dir, Vector3fc up, Quaternionf dest); + + /** + * Apply a rotation to this quaternion that maps the given direction to the positive Z axis, and store the result in dest. + *

+ * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain + * parallel to the plane spanned by the up and dir vectors. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: http://answers.unity3d.com + * + * @param dirX + * the x-coordinate of the direction to look along + * @param dirY + * the y-coordinate of the direction to look along + * @param dirZ + * the z-coordinate of the direction to look along + * @param upX + * the x-coordinate of the up vector + * @param upY + * the y-coordinate of the up vector + * @param upZ + * the z-coordinate of the up vector + * @param dest + * will hold the result + * @return dest + */ + Quaternionf lookAlong(float dirX, float dirY, float dirZ, float upX, float upY, float upZ, Quaternionf dest); + + /** + * Apply a rotation to this that rotates the fromDir vector to point along toDir and + * store the result in dest. + *

+ * Since there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + *

+ * Reference: stackoverflow.com + * + * @param fromDirX + * the x-coordinate of the direction to rotate into the destination direction + * @param fromDirY + * the y-coordinate of the direction to rotate into the destination direction + * @param fromDirZ + * the z-coordinate of the direction to rotate into the destination direction + * @param toDirX + * the x-coordinate of the direction to rotate to + * @param toDirY + * the y-coordinate of the direction to rotate to + * @param toDirZ + * the z-coordinate of the direction to rotate to + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateTo(float fromDirX, float fromDirY, float fromDirZ, float toDirX, float toDirY, float toDirZ, Quaternionf dest); + + /** + * Apply a rotation to this that rotates the fromDir vector to point along toDir and + * store the result in dest. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateTo(float, float, float, float, float, float, Quaternionf) + * + * @param fromDir + * the starting direction + * @param toDir + * the destination direction + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateTo(Vector3fc fromDir, Vector3fc toDir, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the x axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the x axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateX(float angle, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the y axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the y axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateY(float angle, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the z axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the z axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateZ(float angle, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the local x axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local x axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateLocalX(float angle, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the local y axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local y axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateLocalY(float angle, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the local z axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be R * Q. So when transforming a + * vector v with the new quaternion by using R * Q * v, the + * rotation represented by this will be applied first! + * + * @param angle + * the angle in radians to rotate about the local z axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateLocalZ(float angle, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the specified axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @param angle + * the angle in radians to rotate about the specified axis + * @param axisX + * the x coordinate of the rotation axis + * @param axisY + * the y coordinate of the rotation axis + * @param axisZ + * the z coordinate of the rotation axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateAxis(float angle, float axisX, float axisY, float axisZ, Quaternionf dest); + + /** + * Apply a rotation to this quaternion rotating the given radians about the specified axis + * and store the result in dest. + *

+ * If Q is this quaternion and R the quaternion representing the + * specified rotation, then the new quaternion will be Q * R. So when transforming a + * vector v with the new quaternion by using Q * R * v, the + * rotation added by this method will be applied first! + * + * @see #rotateAxis(float, float, float, float, Quaternionf) + * + * @param angle + * the angle in radians to rotate about the specified axis + * @param axis + * the rotation axis + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotateAxis(float angle, Vector3fc axis, Quaternionf dest); + + /** + * Compute the difference between this and the other quaternion + * and store the result in dest. + *

+ * The difference is the rotation that has to be applied to get from + * this rotation to other. If T is this, Q + * is other and D is the computed difference, then the following equation holds: + *

+ * T * D = Q + *

+ * It is defined as: D = T^-1 * Q, where T^-1 denotes the {@link #invert(Quaternionf) inverse} of T. + * + * @param other + * the other quaternion + * @param dest + * will hold the result + * @return dest + */ + Quaternionf difference(Quaternionfc other, Quaternionf dest); + + /** + * Obtain the direction of +X before the rotation transformation represented by this quaternion is applied. + *

+ * This method is equivalent to the following code: + *

+     * Quaternionf inv = new Quaternionf(this).invert();
+     * inv.transform(dir.set(1, 0, 0));
+     * 
+ * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3f positiveX(Vector3f dir); + + /** + * Obtain the direction of +X before the rotation transformation represented by this normalized quaternion is applied. + * The quaternion must be {@link #normalize(Quaternionf) normalized} for this method to work. + *

+ * This method is equivalent to the following code: + *

+     * Quaternionf inv = new Quaternionf(this).conjugate();
+     * inv.transform(dir.set(1, 0, 0));
+     * 
+ * + * @param dir + * will hold the direction of +X + * @return dir + */ + Vector3f normalizedPositiveX(Vector3f dir); + + /** + * Obtain the direction of +Y before the rotation transformation represented by this quaternion is applied. + *

+ * This method is equivalent to the following code: + *

+     * Quaternionf inv = new Quaternionf(this).invert();
+     * inv.transform(dir.set(0, 1, 0));
+     * 
+ * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3f positiveY(Vector3f dir); + + /** + * Obtain the direction of +Y before the rotation transformation represented by this normalized quaternion is applied. + * The quaternion must be {@link #normalize(Quaternionf) normalized} for this method to work. + *

+ * This method is equivalent to the following code: + *

+     * Quaternionf inv = new Quaternionf(this).conjugate();
+     * inv.transform(dir.set(0, 1, 0));
+     * 
+ * + * @param dir + * will hold the direction of +Y + * @return dir + */ + Vector3f normalizedPositiveY(Vector3f dir); + + /** + * Obtain the direction of +Z before the rotation transformation represented by this quaternion is applied. + *

+ * This method is equivalent to the following code: + *

+     * Quaternionf inv = new Quaternionf(this).invert();
+     * inv.transform(dir.set(0, 0, 1));
+     * 
+ * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3f positiveZ(Vector3f dir); + + /** + * Obtain the direction of +Z before the rotation transformation represented by this normalized quaternion is applied. + * The quaternion must be {@link #normalize(Quaternionf) normalized} for this method to work. + *

+ * This method is equivalent to the following code: + *

+     * Quaternionf inv = new Quaternionf(this).conjugate();
+     * inv.transform(dir.set(0, 0, 1));
+     * 
+ * + * @param dir + * will hold the direction of +Z + * @return dir + */ + Vector3f normalizedPositiveZ(Vector3f dir); + + /** + * Conjugate this by the given quaternion q by computing q * this * q^-1 + * and store the result into dest. + * + * @param q + * the {@link Quaternionfc} to conjugate this by + * @param dest + * will hold the result + * @return dest + */ + Quaternionf conjugateBy(Quaternionfc q, Quaternionf dest); + + /** + * Determine whether all components are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + Compare the quaternion components of this quaternion with the given quaternion using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param q + * the other quaternion + * @param delta + * the allowed maximum difference + * @return true whether all of the quaternion components are equal; false otherwise + */ + boolean equals(Quaternionfc q, float delta); + + /** + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @param z + * the z component to compare to + * @param w + * the w component to compare to + * @return true if all the quaternion components are equal + */ + boolean equals(float x, float y, float z, float w); +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Random.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Random.java new file mode 100644 index 000000000..4ee09ab7f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Random.java @@ -0,0 +1,169 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Pseudo-random number generator. + * + * @author Kai Burjack + */ +public class Random { + + /** + * Reference http://xoroshiro.di.unimi.it/ + */ + private static final class Xorshiro128 { + /** + * = 0x1p-24f + */ + private static final float INT_TO_FLOAT = Float.intBitsToFloat(864026624); + + /** + * Xorshiro128 state + */ + private long _s0; + private long _s1; + + /** + * SplitMix64 State + */ + private long state; + + Xorshiro128(long seed) { + this.state = seed; + this._s0 = nextSplitMix64(); + this._s1 = nextSplitMix64(); + } + + private long nextSplitMix64() { + long z = state += 0x9e3779b97f4a7c15L; + z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L; + z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL; + return z ^ (z >>> 31); + } + + /** + * Reference: https://github.com/roquendm/ + * + * @author roquendm + */ + final float nextFloat() { + return (nextInt() >>> 8) * INT_TO_FLOAT; + } + + private int nextInt() { + long s0 = _s0; + long s1 = _s1; + long result = s0 + s1; + s1 ^= s0; + rotateLeft(s0, s1); + return (int) (result & 0xFFFFFFFF); + } + private static long rotl_JDK4(final long x, final int k) { + return (x << k) | (x >>> (64 - k)); + } + private static long rotl_JDK5(final long x, final int k) { + return Long.rotateLeft(x, k); + } + private static long rotl(final long x, final int k) { + if (Runtime.HAS_Long_rotateLeft) + return rotl_JDK5(x, k); + return rotl_JDK4(x, k); + } + private void rotateLeft(long s0, long s1) { + _s0 = rotl(s0, 55) ^ s1 ^ (s1 << 14); + _s1 = rotl(s1, 36); + } + + /** + * Reference: https://github.com/roquendm/ + * + * @author roquendm + */ + final int nextInt(int n) { + // See notes in nextInt. This is + // (on average) a better choice for + // 64-bit VMs. + long r = nextInt() >>> 1; + // sign doesn't matter here + r = (r * n) >> 31; + return (int) r; + } + } + + private final Xorshiro128 rnd; + + //8020463840 is from "Case File n_221: Kabukicho" + private static long seedHalf = 8020463840L; + + public static long newSeed() { + // 3512401965023503517 is from L'Ecuyer, "Tables of Linear Congruential Generators of + // Different Sizes and Good Lattice Structure", 1999 + long oldSeedHalf, newSeedHalf; + synchronized (Random.class) { + oldSeedHalf = seedHalf; + newSeedHalf = oldSeedHalf * 3512401965023503517L; + seedHalf = newSeedHalf; + } + return newSeedHalf; + } + + /** + * Create a new instance of {@link Random} and initialize it with a random seed. + */ + public Random() { + this(newSeed() ^ System.nanoTime()); + } + + /** + * Create a new instance of {@link Random} and initialize it with the given seed. + * + * @param seed + * the seed number + */ + public Random(long seed) { + this.rnd = new Xorshiro128(seed); + } + + /** + * Generate a uniformly distributed floating-point number in the half-open range [0, 1). + * + * @return a random float in the range [0..1) + */ + public float nextFloat() { + return rnd.nextFloat(); + } + + /** + * Generate a uniformly distributed integer in the half-open range [0, n). + * + * @param n + * the upper limit (exclusive) of the generated integer + * @return a random integer in the range [0..n) + */ + public int nextInt(int n) { + return rnd.nextInt(n); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/RayAabIntersection.java b/src/main/java/com/jozufozu/flywheel/repack/joml/RayAabIntersection.java new file mode 100644 index 000000000..6d635cb12 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/RayAabIntersection.java @@ -0,0 +1,399 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Kai Burjack + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * This is an implementation of the Fast Ray/Axis-Aligned Bounding Box + * Overlap Tests using Ray Slopes paper. + *

+ * It is an efficient implementation when testing many axis-aligned boxes against the same ray. + *

+ * This class is thread-safe and can be used in a multithreaded environment when testing many axis-aligned boxes against the same ray concurrently. + * + * @author Kai Burjack + */ +public class RayAabIntersection { + private float originX, originY, originZ; + private float dirX, dirY, dirZ; + + /* Needed for ray slope intersection method */ + private float c_xy, c_yx, c_zy, c_yz, c_xz, c_zx; + private float s_xy, s_yx, s_zy, s_yz, s_xz, s_zx; + private byte classification; + + /** + * Create a new {@link RayAabIntersection} without initializing a ray. + *

+ * Before using the {@link #test(float, float, float, float, float, float) intersect()} method, + * the method {@link #set(float, float, float, float, float, float) set()} must be called in order to + * initialize the created RayAabIntersection instance with a ray. + * + * @see #set(float, float, float, float, float, float) + */ + public RayAabIntersection() { + } + + /** + * Create a new {@link RayAabIntersection} and initialize it with a ray with origin (originX, originY, originZ) + * and direction (dirX, dirY, dirZ). + *

+ * In order to change the direction and/or origin of the ray later, use {@link #set(float, float, float, float, float, float) set()}. + * + * @see #set(float, float, float, float, float, float) + * + * @param originX + * the x coordinate of the origin + * @param originY + * the y coordinate of the origin + * @param originZ + * the z coordinate of the origin + * @param dirX + * the x coordinate of the direction + * @param dirY + * the y coordinate of the direction + * @param dirZ + * the z coordinate of the direction + */ + public RayAabIntersection(float originX, float originY, float originZ, float dirX, float dirY, float dirZ) { + set(originX, originY, originZ, dirX, dirY, dirZ); + } + + /** + * Update the ray stored by this {@link RayAabIntersection} with the new origin (originX, originY, originZ) + * and direction (dirX, dirY, dirZ). + * + * @param originX + * the x coordinate of the ray origin + * @param originY + * the y coordinate of the ray origin + * @param originZ + * the z coordinate of the ray origin + * @param dirX + * the x coordinate of the ray direction + * @param dirY + * the y coordinate of the ray direction + * @param dirZ + * the z coordinate of the ray direction + */ + public void set(float originX, float originY, float originZ, float dirX, float dirY, float dirZ) { + this.originX = originX; + this.originY = originY; + this.originZ = originZ; + this.dirX = dirX; + this.dirY = dirY; + this.dirZ = dirZ; + precomputeSlope(); + } + + private static int signum(float f) { + return (f == 0.0f || Float.isNaN(f)) ? 0 : ((1 - Float.floatToIntBits(f) >>> 31) << 1) - 1; + } + + /** + * Precompute the values necessary for the ray slope algorithm. + */ + private void precomputeSlope() { + float invDirX = 1.0f / dirX; + float invDirY = 1.0f / dirY; + float invDirZ = 1.0f / dirZ; + s_yx = dirX * invDirY; + s_xy = dirY * invDirX; + s_zy = dirY * invDirZ; + s_yz = dirZ * invDirY; + s_xz = dirZ * invDirX; + s_zx = dirX * invDirZ; + c_xy = originY - s_xy * originX; + c_yx = originX - s_yx * originY; + c_zy = originY - s_zy * originZ; + c_yz = originZ - s_yz * originY; + c_xz = originZ - s_xz * originX; // <- original paper had a bug here. It switched originZ/originX + c_zx = originX - s_zx * originZ; // <- original paper had a bug here. It switched originZ/originX + int sgnX = signum(dirX); + int sgnY = signum(dirY); + int sgnZ = signum(dirZ); + classification = (byte) ((sgnZ+1) << 4 | (sgnY+1) << 2 | (sgnX+1)); + } + + /** + * Test whether the ray stored in this {@link RayAabIntersection} intersect the axis-aligned box + * given via its minimum corner (minX, minY, minZ) and its maximum corner (maxX, maxY, maxZ). + *

+ * This implementation uses a tableswitch to dispatch to the correct intersection method. + *

+ * This method is thread-safe and can be used to test many axis-aligned boxes concurrently. + * + * @param minX + * the x coordinate of the minimum corner + * @param minY + * the y coordinate of the minimum corner + * @param minZ + * the z coordinate of the minimum corner + * @param maxX + * the x coordinate of the maximum corner + * @param maxY + * the y coordinate of the maximum corner + * @param maxZ + * the z coordinate of the maximum corner + * @return true iff the ray intersects the given axis-aligned box; false otherwise + */ + public boolean test(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + // tableswitch with dense and consecutive cases (will be a simple jump based on the switch argument) + switch (classification) { + case 0: // 0b000000: // MMM + return MMM(minX, minY, minZ, maxX, maxY, maxZ); + case 1: // 0b000001: // OMM + return OMM(minX, minY, minZ, maxX, maxY, maxZ); + case 2: // 0b000010: // PMM + return PMM(minX, minY, minZ, maxX, maxY, maxZ); + case 3: // 0b000011: // not used + return false; + case 4: // 0b000100: // MOM + return MOM(minX, minY, minZ, maxX, maxY, maxZ); + case 5: // 0b000101: // OOM + return OOM(minX, minY, minZ, maxX, maxY); + case 6: // 0b000110: // POM + return POM(minX, minY, minZ, maxX, maxY, maxZ); + case 7: // 0b000111: // not used + return false; + case 8: // 0b001000: // MPM + return MPM(minX, minY, minZ, maxX, maxY, maxZ); + case 9: // 0b001001: // OPM + return OPM(minX, minY, minZ, maxX, maxY, maxZ); + case 10: // 0b001010: // PPM + return PPM(minX, minY, minZ, maxX, maxY, maxZ); + case 11: // 0b001011: // not used + case 12: // 0b001100: // not used + case 13: // 0b001101: // not used + case 14: // 0b001110: // not used + case 15: // 0b001111: // not used + return false; + case 16: // 0b010000: // MMO + return MMO(minX, minY, minZ, maxX, maxY, maxZ); + case 17: // 0b010001: // OMO + return OMO(minX, minY, minZ, maxX, maxZ); + case 18: // 0b010010: // PMO + return PMO(minX, minY, minZ, maxX, maxY, maxZ); + case 19: // 0b010011: // not used + return false; + case 20: // 0b010100: // MOO + return MOO(minX, minY, minZ, maxY, maxZ); + case 21: // 0b010101: // OOO + return false; // <- degenerate case + case 22: // 0b010110: // POO + return POO(minY, minZ, maxX, maxY, maxZ); + case 23: // 0b010111: // not used + return false; + case 24: // 0b011000: // MPO + return MPO(minX, minY, minZ, maxX, maxY, maxZ); + case 25: // 0b011001: // OPO + return OPO(minX, minZ, maxX, maxY, maxZ); + case 26: // 0b011010: // PPO + return PPO(minX, minY, minZ, maxX, maxY, maxZ); + case 27: // 0b011011: // not used + case 28: // 0b011100: // not used + case 29: // 0b011101: // not used + case 30: // 0b011110: // not used + case 31: // 0b011111: // not used + return false; + case 32: // 0b100000: // MMP + return MMP(minX, minY, minZ, maxX, maxY, maxZ); + case 33: // 0b100001: // OMP + return OMP(minX, minY, minZ, maxX, maxY, maxZ); + case 34: // 0b100010: // PMP + return PMP(minX, minY, minZ, maxX, maxY, maxZ); + case 35: // 0b100011: // not used + return false; + case 36: // 0b100100: // MOP + return MOP(minX, minY, minZ, maxX, maxY, maxZ); + case 37: // 0b100101: // OOP + return OOP(minX, minY, maxX, maxY, maxZ); + case 38: // 0b100110: // POP + return POP(minX, minY, minZ, maxX, maxY, maxZ); + case 39: // 0b100111: // not used + return false; + case 40: // 0b101000: // MPP + return MPP(minX, minY, minZ, maxX, maxY, maxZ); + case 41: // 0b101001: // OPP + return OPP(minX, minY, minZ, maxX, maxY, maxZ); + case 42: // 0b101010: // PPP + return PPP(minX, minY, minZ, maxX, maxY, maxZ); + default: + return false; + } + } + + /* Intersection tests for all possible ray direction cases */ + + private boolean MMM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX >= minX && originY >= minY && originZ >= minZ + && s_xy * minX - maxY + c_xy <= 0.0f + && s_yx * minY - maxX + c_yx <= 0.0f + && s_zy * minZ - maxY + c_zy <= 0.0f + && s_yz * minY - maxZ + c_yz <= 0.0f + && s_xz * minX - maxZ + c_xz <= 0.0f + && s_zx * minZ - maxX + c_zx <= 0.0f; + } + private boolean OMM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX >= minX && originX <= maxX && originY >= minY && originZ >= minZ + && s_zy * minZ - maxY + c_zy <= 0.0f + && s_yz * minY - maxZ + c_yz <= 0.0f; + } + private boolean PMM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX <= maxX && originY >= minY && originZ >= minZ + && s_xy * maxX - maxY + c_xy <= 0.0f + && s_yx * minY - minX + c_yx >= 0.0f + && s_zy * minZ - maxY + c_zy <= 0.0f + && s_yz * minY - maxZ + c_yz <= 0.0f + && s_xz * maxX - maxZ + c_xz <= 0.0f + && s_zx * minZ - minX + c_zx >= 0.0f; + } + private boolean MOM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originY >= minY && originY <= maxY && originX >= minX && originZ >= minZ + && s_xz * minX - maxZ + c_xz <= 0.0f + && s_zx * minZ - maxX + c_zx <= 0.0f; + } + private boolean OOM(float minX, float minY, float minZ, float maxX, float maxY) { + return originZ >= minZ && originX >= minX && originX <= maxX && originY >= minY && originY <= maxY; + } + private boolean POM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originY >= minY && originY <= maxY && originX <= maxX && originZ >= minZ + && s_xz * maxX - maxZ + c_xz <= 0.0f + && s_zx * minZ - minX + c_zx >= 0.0f; + } + private boolean MPM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX >= minX && originY <= maxY && originZ >= minZ + && s_xy * minX - minY + c_xy >= 0.0f + && s_yx * maxY - maxX + c_yx <= 0.0f + && s_zy * minZ - minY + c_zy >= 0.0f + && s_yz * maxY - maxZ + c_yz <= 0.0f + && s_xz * minX - maxZ + c_xz <= 0.0f + && s_zx * minZ - maxX + c_zx <= 0.0f; + } + private boolean OPM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX >= minX && originX <= maxX && originY <= maxY && originZ >= minZ + && s_zy * minZ - minY + c_zy >= 0.0f + && s_yz * maxY - maxZ + c_yz <= 0.0f; + } + private boolean PPM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX <= maxX && originY <= maxY && originZ >= minZ + && s_xy * maxX - minY + c_xy >= 0.0f + && s_yx * maxY - minX + c_yx >= 0.0f + && s_zy * minZ - minY + c_zy >= 0.0f + && s_yz * maxY - maxZ + c_yz <= 0.0f + && s_xz * maxX - maxZ + c_xz <= 0.0f + && s_zx * minZ - minX + c_zx >= 0.0f; + } + private boolean MMO(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originZ >= minZ && originZ <= maxZ && originX >= minX && originY >= minY + && s_xy * minX - maxY + c_xy <= 0.0f + && s_yx * minY - maxX + c_yx <= 0.0f; + } + private boolean OMO(float minX, float minY, float minZ, float maxX, float maxZ) { + return originY >= minY && originX >= minX && originX <= maxX && originZ >= minZ && originZ <= maxZ; + } + private boolean PMO(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originZ >= minZ && originZ <= maxZ && originX <= maxX && originY >= minY + && s_xy * maxX - maxY + c_xy <= 0.0f + && s_yx * minY - minX + c_yx >= 0.0f; + } + private boolean MOO(float minX, float minY, float minZ, float maxY, float maxZ) { + return originX >= minX && originY >= minY && originY <= maxY && originZ >= minZ && originZ <= maxZ; + } + private boolean POO(float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX <= maxX && originY >= minY && originY <= maxY && originZ >= minZ && originZ <= maxZ; + } + private boolean MPO(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originZ >= minZ && originZ <= maxZ && originX >= minX && originY <= maxY + && s_xy * minX - minY + c_xy >= 0.0f + && s_yx * maxY - maxX + c_yx <= 0.0f; + } + private boolean OPO(float minX, float minZ, float maxX, float maxY, float maxZ) { + return originY <= maxY && originX >= minX && originX <= maxX && originZ >= minZ && originZ <= maxZ; + } + private boolean PPO(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originZ >= minZ && originZ <= maxZ && originX <= maxX && originY <= maxY + && s_xy * maxX - minY + c_xy >= 0.0f + && s_yx * maxY - minX + c_yx >= 0.0f; + } + private boolean MMP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX >= minX && originY >= minY && originZ <= maxZ + && s_xy * minX - maxY + c_xy <= 0.0f + && s_yx * minY - maxX + c_yx <= 0.0f + && s_zy * maxZ - maxY + c_zy <= 0.0f + && s_yz * minY - minZ + c_yz >= 0.0f + && s_xz * minX - minZ + c_xz >= 0.0f + && s_zx * maxZ - maxX + c_zx <= 0.0f; + } + private boolean OMP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX >= minX && originX <= maxX && originY >= minY && originZ <= maxZ + && s_zy * maxZ - maxY + c_zy <= 0.0f + && s_yz * minY - minZ + c_yz >= 0.0f; + } + private boolean PMP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX <= maxX && originY >= minY && originZ <= maxZ + && s_xy * maxX - maxY + c_xy <= 0.0f + && s_yx * minY - minX + c_yx >= 0.0f + && s_zy * maxZ - maxY + c_zy <= 0.0f + && s_yz * minY - minZ + c_yz >= 0.0f + && s_xz * maxX - minZ + c_xz >= 0.0f + && s_zx * maxZ - minX + c_zx >= 0.0f; + } + private boolean MOP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originY >= minY && originY <= maxY && originX >= minX && originZ <= maxZ + && s_xz * minX - minZ + c_xz >= 0.0f + && s_zx * maxZ - maxX + c_zx <= 0.0f; + } + private boolean OOP(float minX, float minY, float maxX, float maxY, float maxZ) { + return originZ <= maxZ && originX >= minX && originX <= maxX && originY >= minY && originY <= maxY; + } + private boolean POP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originY >= minY && originY <= maxY && originX <= maxX && originZ <= maxZ + && s_xz * maxX - minZ + c_xz >= 0.0f + && s_zx * maxZ - minX + c_zx <= 0.0f; + } + private boolean MPP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX >= minX && originY <= maxY && originZ <= maxZ + && s_xy * minX - minY + c_xy >= 0.0f + && s_yx * maxY - maxX + c_yx <= 0.0f + && s_zy * maxZ - minY + c_zy >= 0.0f + && s_yz * maxY - minZ + c_yz >= 0.0f + && s_xz * minX - minZ + c_xz >= 0.0f + && s_zx * maxZ - maxX + c_zx <= 0.0f; + } + private boolean OPP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX >= minX && originX <= maxX && originY <= maxY && originZ <= maxZ + && s_zy * maxZ - minY + c_zy <= 0.0f + && s_yz * maxY - minZ + c_yz <= 0.0f; + } + private boolean PPP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + return originX <= maxX && originY <= maxY && originZ <= maxZ + && s_xy * maxX - minY + c_xy >= 0.0f + && s_yx * maxY - minX + c_yx >= 0.0f + && s_zy * maxZ - minY + c_zy >= 0.0f + && s_yz * maxY - minZ + c_yz >= 0.0f + && s_xz * maxX - minZ + c_xz >= 0.0f + && s_zx * maxZ - minX + c_zx >= 0.0f; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/RoundingMode.java b/src/main/java/com/jozufozu/flywheel/repack/joml/RoundingMode.java new file mode 100644 index 000000000..ef1036956 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/RoundingMode.java @@ -0,0 +1,60 @@ +/* + * The MIT License + * + * Copyright (c) 2020-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * Rounding modes. + * + * @author Kai Burjack + */ +public class RoundingMode { + private RoundingMode() {} + /** + * Discards the fractional part. + */ + public static final int TRUNCATE = 0; + /** + * Round towards positive infinity. + */ + public static final int CEILING = 1; + /** + * Round towards negative infinity. + */ + public static final int FLOOR = 2; + /** + * Round towards the nearest neighbor. If both neighbors are equidistant, round + * towards the even neighbor. + */ + public static final int HALF_EVEN = 3; + /** + * Round towards the nearest neighbor. If both neighbors are equidistant, round + * down. + */ + public static final int HALF_DOWN = 4; + /** + * Round towards the nearest neighbor. If both neighbors are equidistant, round + * up. + */ + public static final int HALF_UP = 5; +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Runtime.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Runtime.java new file mode 100644 index 000000000..d6bd9b9d3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Runtime.java @@ -0,0 +1,148 @@ +/* + * The MIT License + * + * Copyright (c) 2017-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.text.NumberFormat; + +/** + * Internal class to detect features of the runtime. + * + * @author Kai Burjack + */ +public final class Runtime { + + public static final boolean HAS_floatToRawIntBits = hasFloatToRawIntBits(); + public static final boolean HAS_doubleToRawLongBits = hasDoubleToRawLongBits(); + public static final boolean HAS_Long_rotateLeft = hasLongRotateLeft(); + public static final boolean HAS_Math_fma = Options.USE_MATH_FMA && hasMathFma(); + + private static boolean hasMathFma() { + try { + java.lang.Math.class.getDeclaredMethod("fma", new Class[] { float.class, float.class, float.class }); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + private Runtime() { + } + + private static boolean hasFloatToRawIntBits() { + try { + Float.class.getDeclaredMethod("floatToRawIntBits", new Class[] { float.class }); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + private static boolean hasDoubleToRawLongBits() { + try { + Double.class.getDeclaredMethod("doubleToRawLongBits", new Class[] { double.class }); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + private static boolean hasLongRotateLeft() { + try { + Long.class.getDeclaredMethod("rotateLeft", new Class[] { long.class, int.class }); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + public static int floatToIntBits(float flt) { + if (HAS_floatToRawIntBits) + return floatToIntBits1_3(flt); + return floatToIntBits1_2(flt); + } + private static int floatToIntBits1_3(float flt) { + return Float.floatToRawIntBits(flt); + } + private static int floatToIntBits1_2(float flt) { + return Float.floatToIntBits(flt); + } + + public static long doubleToLongBits(double dbl) { + if (HAS_doubleToRawLongBits) + return doubleToLongBits1_3(dbl); + return doubleToLongBits1_2(dbl); + } + private static long doubleToLongBits1_3(double dbl) { + return Double.doubleToRawLongBits(dbl); + } + private static long doubleToLongBits1_2(double dbl) { + return Double.doubleToLongBits(dbl); + } + + public static String formatNumbers(String str) { + StringBuffer res = new StringBuffer(); + int eIndex = Integer.MIN_VALUE; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == 'E') { + eIndex = i; + } else if (c == ' ' && eIndex == i - 1) { + // workaround Java 1.4 DecimalFormat bug + res.append('+'); + continue; + } else if (Character.isDigit(c) && eIndex == i - 1) { + res.append('+'); + } + res.append(c); + } + return res.toString(); + } + + public static String format(double number, NumberFormat format) { + if (Double.isNaN(number)) { + return padLeft(format, " NaN"); + } else if (Double.isInfinite(number)) { + return padLeft(format, number > 0.0 ? " +Inf" : " -Inf"); + } + return format.format(number); + } + + private static String padLeft(NumberFormat format, String str) { + int len = format.format(0.0).length(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < len - str.length() + 1; i++) { + sb.append(" "); + } + return sb.append(str).toString(); + } + + public static boolean equals(float a, float b, float delta) { + return Float.floatToIntBits(a) == Float.floatToIntBits(b) || Math.abs(a - b) <= delta; + } + + public static boolean equals(double a, double b, double delta) { + return Double.doubleToLongBits(a) == Double.doubleToLongBits(b) || Math.abs(a - b) <= delta; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/SimplexNoise.java b/src/main/java/com/jozufozu/flywheel/repack/joml/SimplexNoise.java new file mode 100644 index 000000000..4b378a335 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/SimplexNoise.java @@ -0,0 +1,485 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +/** + * A simplex noise algorithm for 2D, 3D and 4D input. + *

+ * It was originally authored by Stefan Gustavson. + *

+ * The original implementation can be found here: http://http://staffwww.itn.liu.se/. + */ +public class SimplexNoise { + private static class Vector3b { + byte x, y, z; + Vector3b(int x, int y, int z) { + super(); + this.x = (byte) x; + this.y = (byte) y; + this.z = (byte) z; + } + } + private static class Vector4b { + byte x, y, z, w; + Vector4b(int x, int y, int z, int w) { + super(); + this.x = (byte) x; + this.y = (byte) y; + this.z = (byte) z; + this.w = (byte) w; + } + } + + // Kai Burjack: + // Use a three-component vector here to save memory. (instead of using 4-component 'Grad' class) + // And as the original author mentioned on the 'Grad' class, using a class to store the gradient components + // is indeed faster compared to using a simple int[] array... + private static final Vector3b[] grad3 = { new Vector3b(1, 1, 0), new Vector3b(-1, 1, 0), new Vector3b(1, -1, 0), new Vector3b(-1, -1, 0), + new Vector3b(1, 0, 1), new Vector3b(-1, 0, 1), new Vector3b(1, 0, -1), new Vector3b(-1, 0, -1), new Vector3b(0, 1, 1), new Vector3b(0, -1, 1), + new Vector3b(0, 1, -1), new Vector3b(0, -1, -1) }; + + // Kai Burjack: + // As the original author mentioned on the 'Grad' class, using a class to store the gradient components + // is indeed faster compared to using a simple int[] array... + private static final Vector4b[] grad4 = { new Vector4b(0, 1, 1, 1), new Vector4b(0, 1, 1, -1), new Vector4b(0, 1, -1, 1), new Vector4b(0, 1, -1, -1), + new Vector4b(0, -1, 1, 1), new Vector4b(0, -1, 1, -1), new Vector4b(0, -1, -1, 1), new Vector4b(0, -1, -1, -1), new Vector4b(1, 0, 1, 1), + new Vector4b(1, 0, 1, -1), new Vector4b(1, 0, -1, 1), new Vector4b(1, 0, -1, -1), new Vector4b(-1, 0, 1, 1), new Vector4b(-1, 0, 1, -1), + new Vector4b(-1, 0, -1, 1), new Vector4b(-1, 0, -1, -1), new Vector4b(1, 1, 0, 1), new Vector4b(1, 1, 0, -1), new Vector4b(1, -1, 0, 1), + new Vector4b(1, -1, 0, -1), new Vector4b(-1, 1, 0, 1), new Vector4b(-1, 1, 0, -1), new Vector4b(-1, -1, 0, 1), new Vector4b(-1, -1, 0, -1), + new Vector4b(1, 1, 1, 0), new Vector4b(1, 1, -1, 0), new Vector4b(1, -1, 1, 0), new Vector4b(1, -1, -1, 0), new Vector4b(-1, 1, 1, 0), + new Vector4b(-1, 1, -1, 0), new Vector4b(-1, -1, 1, 0), new Vector4b(-1, -1, -1, 0) }; + + // Kai Burjack: + // Use a byte[] instead of a short[] to save memory + private static final byte[] p = { -105, -96, -119, 91, 90, 15, -125, 13, -55, 95, 96, 53, -62, -23, 7, -31, -116, 36, 103, 30, 69, -114, 8, 99, 37, -16, + 21, 10, 23, -66, 6, -108, -9, 120, -22, 75, 0, 26, -59, 62, 94, -4, -37, -53, 117, 35, 11, 32, 57, -79, 33, 88, -19, -107, 56, 87, -82, 20, 125, + -120, -85, -88, 68, -81, 74, -91, 71, -122, -117, 48, 27, -90, 77, -110, -98, -25, 83, 111, -27, 122, 60, -45, -123, -26, -36, 105, 92, 41, 55, 46, + -11, 40, -12, 102, -113, 54, 65, 25, 63, -95, 1, -40, 80, 73, -47, 76, -124, -69, -48, 89, 18, -87, -56, -60, -121, -126, 116, -68, -97, 86, -92, + 100, 109, -58, -83, -70, 3, 64, 52, -39, -30, -6, 124, 123, 5, -54, 38, -109, 118, 126, -1, 82, 85, -44, -49, -50, 59, -29, 47, 16, 58, 17, -74, + -67, 28, 42, -33, -73, -86, -43, 119, -8, -104, 2, 44, -102, -93, 70, -35, -103, 101, -101, -89, 43, -84, 9, -127, 22, 39, -3, 19, 98, 108, 110, + 79, 113, -32, -24, -78, -71, 112, 104, -38, -10, 97, -28, -5, 34, -14, -63, -18, -46, -112, 12, -65, -77, -94, -15, 81, 51, -111, -21, -7, 14, -17, + 107, 49, -64, -42, 31, -75, -57, 106, -99, -72, 84, -52, -80, 115, 121, 50, 45, 127, 4, -106, -2, -118, -20, -51, 93, -34, 114, 67, 29, 24, 72, + -13, -115, -128, -61, 78, 66, -41, 61, -100, -76 }; + // To remove the need for index wrapping, float the permutation table length + private static final byte[] perm = new byte[512]; + private static final byte[] permMod12 = new byte[512]; + static { + for (int i = 0; i < 512; i++) { + perm[i] = p[i & 255]; + permMod12[i] = (byte) ((perm[i]&0xFF) % 12); + } + } + + // Skewing and unskewing factors for 2, 3, and 4 dimensions + private static final float F2 = 0.3660254037844386f; // <- (float) (0.5f * (Math.sqrt(3.0f) - 1.0f)); + private static final float G2 = 0.21132486540518713f; // <- (float) ((3.0f - Math.sqrt(3.0f)) / 6.0f); + private static final float F3 = 1.0f / 3.0f; + private static final float G3 = 1.0f / 6.0f; + private static final float F4 = 0.30901699437494745f; // <- (float) ((Math.sqrt(5.0f) - 1.0f) / 4.0f); + private static final float G4 = 0.1381966011250105f; // <- (float) ((5.0f - Math.sqrt(5.0f)) / 20.0f); + + // This method is a *lot* faster than using (int)Math.floor(x) + private static int fastfloor(float x) { + int xi = (int) x; + return x < xi ? xi - 1 : xi; + } + + private static float dot(Vector3b g, float x, float y) { + return g.x * x + g.y * y; + } + + private static float dot(Vector3b g, float x, float y, float z) { + return g.x * x + g.y * y + g.z * z; + } + + private static float dot(Vector4b g, float x, float y, float z, float w) { + return g.x * x + g.y * y + g.z * z + g.w * w; + } + + /** + * Compute 2D simplex noise for the given input vector (x, y). + *

+ * The result is in the range [-1..+1]. + * + * @param x + * the x coordinate + * @param y + * the y coordinate + * @return the noise value (within [-1..+1]) + */ + public static float noise(float x, float y) { + float n0, n1, n2; // Noise contributions from the three corners + // Skew the input space to determine which simplex cell we're in + float s = (x + y) * F2; // Hairy factor for 2D + int i = fastfloor(x + s); + int j = fastfloor(y + s); + float t = (i + j) * G2; + float X0 = i - t; // Unskew the cell origin back to (x,y) space + float Y0 = j - t; + float x0 = x - X0; // The x,y distances from the cell origin + float y0 = y - Y0; + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if (x0 > y0) { + i1 = 1; + j1 = 0; + } // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else { + i1 = 0; + j1 = 1; + } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + float y1 = y0 - j1 + G2; + float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords + float y2 = y0 - 1.0f + 2.0f * G2; + // Work out the hashed gradient indices of the three simplex corners + int ii = i & 255; + int jj = j & 255; + int gi0 = permMod12[ii + perm[jj]&0xFF]&0xFF; + int gi1 = permMod12[ii + i1 + perm[jj + j1]&0xFF]&0xFF; + int gi2 = permMod12[ii + 1 + perm[jj + 1]&0xFF]&0xFF; + // Calculate the contribution from the three corners + float t0 = 0.5f - x0 * x0 - y0 * y0; + if (t0 < 0.0f) + n0 = 0.0f; + else { + t0 *= t0; + n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient + } + float t1 = 0.5f - x1 * x1 - y1 * y1; + if (t1 < 0.0f) + n1 = 0.0f; + else { + t1 *= t1; + n1 = t1 * t1 * dot(grad3[gi1], x1, y1); + } + float t2 = 0.5f - x2 * x2 - y2 * y2; + if (t2 < 0.0f) + n2 = 0.0f; + else { + t2 *= t2; + n2 = t2 * t2 * dot(grad3[gi2], x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0f * (n0 + n1 + n2); + } + + /** + * Compute 3D simplex noise for the given input vector (x, y, z). + *

+ * The result is in the range [-1..+1]. + * + * @param x + * the x coordinate + * @param y + * the y coordinate + * @param z + * the z coordinate + * @return the noise value (within [-1..+1]) + */ + public static float noise(float x, float y, float z) { + float n0, n1, n2, n3; // Noise contributions from the four corners + // Skew the input space to determine which simplex cell we're in + float s = (x + y + z) * F3; // Very nice and simple skew factor for 3D + int i = fastfloor(x + s); + int j = fastfloor(y + s); + int k = fastfloor(z + s); + float t = (i + j + k) * G3; + float X0 = i - t; // Unskew the cell origin back to (x,y,z) space + float Y0 = j - t; + float Z0 = k - t; + float x0 = x - X0; // The x,y,z distances from the cell origin + float y0 = y - Y0; + float z0 = z - Z0; + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if (x0 >= y0) { + if (y0 >= z0) { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } // X Y Z order + else if (x0 >= z0) { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; + } // X Z Y order + else { + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; + } // Z X Y order + } else { // x0(x, y, z, w). + *

+ * The result is in the range [-1..+1]. + * + * @param x + * the x coordinate + * @param y + * the y coordinate + * @param z + * the z coordinate + * @param w + * the w coordinate + * @return the noise value (within [-1..+1]) + */ + public static float noise(float x, float y, float z, float w) { + float n0, n1, n2, n3, n4; // Noise contributions from the five corners + // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in + float s = (x + y + z + w) * F4; // Factor for 4D skewing + int i = fastfloor(x + s); + int j = fastfloor(y + s); + int k = fastfloor(z + s); + int l = fastfloor(w + s); + float t = (i + j + k + l) * G4; // Factor for 4D unskewing + float X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space + float Y0 = j - t; + float Z0 = k - t; + float W0 = l - t; + float x0 = x - X0; // The x,y,z,w distances from the cell origin + float y0 = y - Y0; + float z0 = z - Z0; + float w0 = w - W0; + // For the 4D case, the simplex is a 4D shape I won't even try to describe. + // To find out which of the 24 possible simplices we're in, we need to + // determine the magnitude ordering of x0, y0, z0 and w0. + // Six pair-wise comparisons are performed between each possible pair + // of the four coordinates, and the results are used to rank the numbers. + int rankx = 0; + int ranky = 0; + int rankz = 0; + int rankw = 0; + if (x0 > y0) + rankx++; + else + ranky++; + if (x0 > z0) + rankx++; + else + rankz++; + if (x0 > w0) + rankx++; + else + rankw++; + if (y0 > z0) + ranky++; + else + rankz++; + if (y0 > w0) + ranky++; + else + rankw++; + if (z0 > w0) + rankz++; + else + rankw++; + int i1, j1, k1, l1; // The integer offsets for the second simplex corner + int i2, j2, k2, l2; // The integer offsets for the third simplex corner + int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; + j1 = ranky >= 3 ? 1 : 0; + k1 = rankz >= 3 ? 1 : 0; + l1 = rankw >= 3 ? 1 : 0; + // Rank 2 denotes the second largest coordinate. + i2 = rankx >= 2 ? 1 : 0; + j2 = ranky >= 2 ? 1 : 0; + k2 = rankz >= 2 ? 1 : 0; + l2 = rankw >= 2 ? 1 : 0; + // Rank 1 denotes the second smallest coordinate. + i3 = rankx >= 1 ? 1 : 0; + j3 = ranky >= 1 ? 1 : 0; + k3 = rankz >= 1 ? 1 : 0; + l3 = rankw >= 1 ? 1 : 0; + // The fifth corner has all coordinate offsets = 1, so no need to compute that. + float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords + float y1 = y0 - j1 + G4; + float z1 = z0 - k1 + G4; + float w1 = w0 - l1 + G4; + float x2 = x0 - i2 + 2.0f * G4; // Offsets for third corner in (x,y,z,w) coords + float y2 = y0 - j2 + 2.0f * G4; + float z2 = z0 - k2 + 2.0f * G4; + float w2 = w0 - l2 + 2.0f * G4; + float x3 = x0 - i3 + 3.0f * G4; // Offsets for fourth corner in (x,y,z,w) coords + float y3 = y0 - j3 + 3.0f * G4; + float z3 = z0 - k3 + 3.0f * G4; + float w3 = w0 - l3 + 3.0f * G4; + float x4 = x0 - 1.0f + 4.0f * G4; // Offsets for last corner in (x,y,z,w) coords + float y4 = y0 - 1.0f + 4.0f * G4; + float z4 = z0 - 1.0f + 4.0f * G4; + float w4 = w0 - 1.0f + 4.0f * G4; + // Work out the hashed gradient indices of the five simplex corners + int ii = i & 255; + int jj = j & 255; + int kk = k & 255; + int ll = l & 255; + int gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]&0xFF]&0xFF]&0xFF]&0xFF) % 32; + int gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]&0xFF]&0xFF]&0xFF]&0xFF) % 32; + int gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]&0xFF]&0xFF]&0xFF]&0xFF) % 32; + int gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]&0xFF]&0xFF]&0xFF]&0xFF) % 32; + int gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]&0xFF]&0xFF]&0xFF]&0xFF) % 32; + // Calculate the contribution from the five corners + float t0 = 0.6f - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; + if (t0 < 0.0f) + n0 = 0.0f; + else { + t0 *= t0; + n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0); + } + float t1 = 0.6f - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; + if (t1 < 0.0f) + n1 = 0.0f; + else { + t1 *= t1; + n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1); + } + float t2 = 0.6f - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; + if (t2 < 0.0f) + n2 = 0.0f; + else { + t2 *= t2; + n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2); + } + float t3 = 0.6f - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; + if (t3 < 0.0f) + n3 = 0.0f; + else { + t3 *= t3; + n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3); + } + float t4 = 0.6f - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; + if (t4 < 0.0f) + n4 = 0.0f; + else { + t4 *= t4; + n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4); + } + // Sum up and scale the result to cover the range [-1,1] + return 27.0f * (n0 + n1 + n2 + n3 + n4); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2d.java new file mode 100644 index 000000000..ccc36cf88 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2d.java @@ -0,0 +1,1364 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Represents a 2D vector with double-precision. + * + * @author RGreenlees + * @author Kai Burjack + * @author F. Neurath + */ +public class Vector2d implements Externalizable, Cloneable, Vector2dc { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public double x; + /** + * The y component of the vector. + */ + public double y; + + /** + * Create a new {@link Vector2d} and initialize its components to zero. + */ + public Vector2d() { + } + + /** + * Create a new {@link Vector2d} and initialize both of its components with the given value. + * + * @param d + * the value of both components + */ + public Vector2d(double d) { + this.x = d; + this.y = d; + } + + /** + * Create a new {@link Vector2d} and initialize its components to the given values. + * + * @param x + * the x value + * @param y + * the y value + */ + public Vector2d(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * Create a new {@link Vector2d} and initialize its components to the one of the given vector. + * + * @param v + * the {@link Vector2dc} to copy the values from + */ + public Vector2d(Vector2dc v) { + x = v.x(); + y = v.y(); + } + + /** + * Create a new {@link Vector2d} and initialize its components to the one of the given vector. + * + * @param v + * the {@link Vector2fc} to copy the values from + */ + public Vector2d(Vector2fc v) { + x = v.x(); + y = v.y(); + } + + /** + * Create a new {@link Vector2d} and initialize its components to the one of the given vector. + * + * @param v + * the {@link Vector2ic} to copy the values from + */ + public Vector2d(Vector2ic v) { + x = v.x(); + y = v.y(); + } + + /** + * Create a new {@link Vector2d} and initialize its two components from the first + * two elements of the given array. + * + * @param xy + * the array containing at least three elements + */ + public Vector2d(double[] xy) { + this.x = xy[0]; + this.y = xy[1]; + } + + /** + * Create a new {@link Vector2d} and initialize its two components from the first + * two elements of the given array. + * + * @param xy + * the array containing at least two elements + */ + public Vector2d(float[] xy) { + this.x = xy[0]; + this.y = xy[1]; + } + + /** + * Create a new {@link Vector2d} and read this vector from the supplied {@link ByteBuffer} + * at the current buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #Vector2d(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y order + * @see #Vector2d(int, ByteBuffer) + */ + public Vector2d(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector2d} and read this vector from the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y order + */ + public Vector2d(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector2d} and read this vector from the supplied {@link DoubleBuffer} + * at the current buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is read, use {@link #Vector2d(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y order + * @see #Vector2d(int, DoubleBuffer) + */ + public Vector2d(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector2d} and read this vector from the supplied {@link DoubleBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * values will be read in x, y order + */ + public Vector2d(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public double x() { + return this.x; + } + + public double y() { + return this.y; + } + + /** + * Set the x and y components to the supplied value. + * + * @param d + * the value of both components + * @return this + */ + public Vector2d set(double d) { + this.x = d; + this.y = d; + return this; + } + + /** + * Set the x and y components to the supplied values. + * + * @param x + * the x value + * @param y + * the y value + * @return this + */ + public Vector2d set(double x, double y) { + this.x = x; + this.y = y; + return this; + } + + /** + * Set this {@link Vector2d} to the values of v. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector2d set(Vector2dc v) { + this.x = v.x(); + this.y = v.y(); + return this; + } + + /** + * Set this {@link Vector2d} to be a clone of v. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector2d set(Vector2fc v) { + this.x = v.x(); + this.y = v.y(); + return this; + } + + /** + * Set this {@link Vector2d} to be a clone of v. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector2d set(Vector2ic v) { + this.x = v.x(); + this.y = v.y(); + return this; + } + + /** + * Set the two components of this vector to the first two elements of the given array. + * + * @param xy + * the array containing at least three elements + * @return this + */ + public Vector2d set(double[] xy) { + this.x = xy[0]; + this.y = xy[1]; + return this; + } + + /** + * Set the two components of this vector to the first two elements of the given array. + * + * @param xy + * the array containing at least two elements + * @return this + */ + public Vector2d set(float[] xy) { + this.x = xy[0]; + this.y = xy[1]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #set(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y order + * @return this + * @see #set(int, ByteBuffer) + */ + public Vector2d set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y order + * @return this + */ + public Vector2d set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is read, use {@link #set(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y order + * @return this + * @see #set(int, DoubleBuffer) + */ + public Vector2d set(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * values will be read in x, y order + * @return this + */ + public Vector2d set(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 2 double values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector2d setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + public double get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + default: + throw new IllegalArgumentException(); + } + } + + public Vector2i get(int mode, Vector2i dest) { + dest.x = Math.roundUsing(this.x(), mode); + dest.y = Math.roundUsing(this.y(), mode); + return dest; + } + + public Vector2f get(Vector2f dest) { + dest.x = (float) this.x(); + dest.y = (float) this.y(); + return dest; + } + + public Vector2d get(Vector2d dest) { + dest.x = this.x(); + dest.y = this.y(); + return dest; + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..1] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..1] + */ + public Vector2d setComponent(int component, double value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public DoubleBuffer get(DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public DoubleBuffer get(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector2dc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Set this vector to be one of its perpendicular vectors. + * + * @return this + */ + public Vector2d perpendicular() { + double xTemp = y; + this.y = x * -1; + this.x = xTemp; + return this; + } + + /** + * Subtract v from this vector. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector2d sub(Vector2dc v) { + this.x = x - v.x(); + this.y = y - v.y(); + return this; + } + + /** + * Subtract (x, y) from this vector. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @return this + */ + public Vector2d sub(double x, double y) { + this.x = this.x - x; + this.y = this.y - y; + return this; + } + + public Vector2d sub(double x, double y, Vector2d dest) { + dest.x = this.x - x; + dest.y = this.y - y; + return dest; + } + + /** + * Subtract v from this vector. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector2d sub(Vector2fc v) { + this.x = x - v.x(); + this.y = y - v.y(); + return this; + } + + public Vector2d sub(Vector2dc v, Vector2d dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + return dest; + } + + public Vector2d sub(Vector2fc v, Vector2d dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + return dest; + } + + /** + * Multiply the components of this vector by the given scalar. + * + * @param scalar + * the value to multiply this vector's components by + * @return this + */ + public Vector2d mul(double scalar) { + this.x = x * scalar; + this.y = y * scalar; + return this; + } + + public Vector2d mul(double scalar, Vector2d dest) { + dest.x = x * scalar; + dest.y = y * scalar; + return dest; + } + + /** + * Multiply the components of this Vector2d by the given scalar values and store the result in this. + * + * @param x + * the x component to multiply this vector by + * @param y + * the y component to multiply this vector by + * @return this + */ + public Vector2d mul(double x, double y) { + this.x = this.x * x; + this.y = this.y * y; + return this; + } + + public Vector2d mul(double x, double y, Vector2d dest) { + dest.x = this.x * x; + dest.y = this.y * y; + return dest; + } + + /** + * Multiply this Vector2d component-wise by another Vector2d. + * + * @param v + * the vector to multiply by + * @return this + */ + public Vector2d mul(Vector2dc v) { + this.x = x * v.x(); + this.y = y * v.y(); + return this; + } + + public Vector2d mul(Vector2dc v, Vector2d dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + return dest; + } + + /** + * Divide this Vector2d by the given scalar value. + * + * @param scalar + * the scalar to divide this vector by + * @return this + */ + public Vector2d div(double scalar) { + double inv = 1.0 / scalar; + this.x = x * inv; + this.y = y * inv; + return this; + } + + public Vector2d div(double scalar, Vector2d dest) { + double inv = 1.0 / scalar; + dest.x = x * inv; + dest.y = y * inv; + return dest; + } + + /** + * Divide the components of this Vector2d by the given scalar values and store the result in this. + * + * @param x + * the x component to divide this vector by + * @param y + * the y component to divide this vector by + * @return this + */ + public Vector2d div(double x, double y) { + this.x = this.x / x; + this.y = this.y / y; + return this; + } + + public Vector2d div(double x, double y, Vector2d dest) { + dest.x = this.x / x; + dest.y = this.y / y; + return dest; + } + + /** + * Divide this Vector2d component-wise by another Vector2dc. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector2d div(Vector2d v) { + this.x = x / v.x(); + this.y = y / v.y(); + return this; + } + + /** + * Divide this Vector3d component-wise by another Vector2fc. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector2d div(Vector2fc v) { + this.x = x / v.x(); + this.y = y / v.y(); + return this; + } + + public Vector2d div(Vector2fc v, Vector2d dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + return dest; + } + + public Vector2d div(Vector2dc v, Vector2d dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + return dest; + } + + /** + * Multiply the given matrix mat with this Vector2d. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector2d mul(Matrix2fc mat) { + double rx = mat.m00() * x + mat.m10() * y; + double ry = mat.m01() * x + mat.m11() * y; + this.x = rx; + this.y = ry; + return this; + } + + /** + * Multiply the given matrix mat with this Vector2d. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector2d mul(Matrix2dc mat) { + double rx = mat.m00() * x + mat.m10() * y; + double ry = mat.m01() * x + mat.m11() * y; + this.x = rx; + this.y = ry; + return this; + } + + public Vector2d mul(Matrix2dc mat, Vector2d dest) { + double rx = mat.m00() * x + mat.m10() * y; + double ry = mat.m01() * x + mat.m11() * y; + dest.x = rx; + dest.y = ry; + return dest; + } + + public Vector2d mul(Matrix2fc mat, Vector2d dest) { + double rx = mat.m00() * x + mat.m10() * y; + double ry = mat.m01() * x + mat.m11() * y; + dest.x = rx; + dest.y = ry; + return dest; + } + + /** + * Multiply the transpose of the given matrix with this Vector2d and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector2d mulTranspose(Matrix2dc mat) { + double rx = mat.m00() * x + mat.m01() * y; + double ry = mat.m10() * x + mat.m11() * y; + this.x = rx; + this.y = ry; + return this; + } + + public Vector2d mulTranspose(Matrix2dc mat, Vector2d dest) { + double rx = mat.m00() * x + mat.m01() * y; + double ry = mat.m10() * x + mat.m11() * y; + dest.x = rx; + dest.y = ry; + return dest; + } + + /** + * Multiply the transpose of the given matrix with this Vector2d and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector2d mulTranspose(Matrix2fc mat) { + double rx = mat.m00() * x + mat.m01() * y; + double ry = mat.m10() * x + mat.m11() * y; + this.x = rx; + this.y = ry; + return this; + } + + public Vector2d mulTranspose(Matrix2fc mat, Vector2d dest) { + double rx = mat.m00() * x + mat.m01() * y; + double ry = mat.m10() * x + mat.m11() * y; + dest.x = rx; + dest.y = ry; + return dest; + } + + /** + * Multiply the given 3x2 matrix mat with this. + *

+ * This method assumes the z component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector2d mulPosition(Matrix3x2dc mat) { + double rx = mat.m00() * x + mat.m10() * y + mat.m20(); + double ry = mat.m01() * x + mat.m11() * y + mat.m21(); + this.x = rx; + this.y = ry; + return this; + } + + public Vector2d mulPosition(Matrix3x2dc mat, Vector2d dest) { + double rx = mat.m00() * x + mat.m10() * y + mat.m20(); + double ry = mat.m01() * x + mat.m11() * y + mat.m21(); + dest.x = rx; + dest.y = ry; + return dest; + } + + /** + * Multiply the given 3x2 matrix mat with this. + *

+ * This method assumes the z component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector2d mulDirection(Matrix3x2dc mat) { + double rx = mat.m00() * x + mat.m10() * y; + double ry = mat.m01() * x + mat.m11() * y; + this.x = rx; + this.y = ry; + return this; + } + + public Vector2d mulDirection(Matrix3x2dc mat, Vector2d dest) { + double rx = mat.m00() * x + mat.m10() * y; + double ry = mat.m01() * x + mat.m11() * y; + dest.x = rx; + dest.y = ry; + return dest; + } + + public double dot(Vector2dc v) { + return x * v.x() + y * v.y(); + } + + public double angle(Vector2dc v) { + double dot = x*v.x() + y*v.y(); + double det = x*v.y() - y*v.x(); + return Math.atan2(det, dot); + } + + public double lengthSquared() { + return x * x + y * y; + } + + /** + * Get the length squared of a 2-dimensional double-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * + * @return the length squared of the given vector + * + * @author F. Neurath + */ + public static double lengthSquared(double x, double y) { + return x * x + y * y; + } + + public double length() { + return Math.sqrt(x * x + y * y); + } + + /** + * Get the length of a 2-dimensional double-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * + * @return the length of the given vector + * + * @author F. Neurath + */ + public static double length(double x, double y) { + return Math.sqrt(x * x + y * y); + } + + public double distance(Vector2dc v) { + double dx = this.x - v.x(); + double dy = this.y - v.y(); + return Math.sqrt(dx * dx + dy * dy); + } + + public double distanceSquared(Vector2dc v) { + double dx = this.x - v.x(); + double dy = this.y - v.y(); + return dx * dx + dy * dy; + } + + public double distance(Vector2fc v) { + double dx = this.x - v.x(); + double dy = this.y - v.y(); + return Math.sqrt(dx * dx + dy * dy); + } + + public double distanceSquared(Vector2fc v) { + double dx = this.x - v.x(); + double dy = this.y - v.y(); + return dx * dx + dy * dy; + } + + public double distance(double x, double y) { + double dx = this.x - x; + double dy = this.y - y; + return Math.sqrt(dx * dx + dy * dy); + } + + public double distanceSquared(double x, double y) { + double dx = this.x - x; + double dy = this.y - y; + return dx * dx + dy * dy; + } + + /** + * Return the distance between (x1, y1) and (x2, y2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @return the euclidean distance + */ + public static double distance(double x1, double y1, double x2, double y2) { + double dx = x1 - x2; + double dy = y1 - y2; + return Math.sqrt(dx * dx + dy * dy); + } + + /** + * Return the squared distance between (x1, y1) and (x2, y2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @return the euclidean distance squared + */ + public static double distanceSquared(double x1, double y1, double x2, double y2) { + double dx = x1 - x2; + double dy = y1 - y2; + return dx * dx + dy * dy; + } + + /** + * Normalize this vector. + * + * @return this + */ + public Vector2d normalize() { + double invLength = Math.invsqrt(x * x + y * y); + this.x = x * invLength; + this.y = y * invLength; + return this; + } + + public Vector2d normalize(Vector2d dest) { + double invLength = Math.invsqrt(x * x + y * y); + dest.x = x * invLength; + dest.y = y * invLength; + return dest; + } + + /** + * Scale this vector to have the given length. + * + * @param length + * the desired length + * @return this + */ + public Vector2d normalize(double length) { + double invLength = Math.invsqrt(x * x + y * y) * length; + this.x = x * invLength; + this.y = y * invLength; + return this; + } + + public Vector2d normalize(double length, Vector2d dest) { + double invLength = Math.invsqrt(x * x + y * y) * length; + dest.x = x * invLength; + dest.y = y * invLength; + return dest; + } + + /** + * Add v to this vector. + * + * @param v + * the vector to add + * @return this + */ + public Vector2d add(Vector2dc v) { + this.x = x + v.x(); + this.y = y + v.y(); + return this; + } + + /** + * Add (x, y) to this vector. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @return this + */ + public Vector2d add(double x, double y) { + this.x = this.x + x; + this.y = this.y + y; + return this; + } + + public Vector2d add(double x, double y, Vector2d dest) { + dest.x = this.x + x; + dest.y = this.y + y; + return dest; + } + + /** + * Add v to this vector. + * + * @param v + * the vector to add + * @return this + */ + public Vector2d add(Vector2fc v) { + this.x = x + v.x(); + this.y = y + v.y(); + return this; + } + + public Vector2d add(Vector2dc v, Vector2d dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + return dest; + } + + public Vector2d add(Vector2fc v, Vector2d dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + return dest; + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector2d zero() { + this.x = 0; + this.y = 0; + return this; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(x); + out.writeDouble(y); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + x = in.readDouble(); + y = in.readDouble(); + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector2d negate() { + this.x = -x; + this.y = -y; + return this; + } + + public Vector2d negate(Vector2d dest) { + dest.x = -x; + dest.y = -y; + return dest; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Vector2d lerp(Vector2dc other, double t) { + this.x = x + (other.x() - x) * t; + this.y = y + (other.y() - y) * t; + return this; + } + + public Vector2d lerp(Vector2dc other, double t, Vector2d dest) { + dest.x = x + (other.x() - x) * t; + dest.y = y + (other.y() - y) * t; + return dest; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(x); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(y); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector2d other = (Vector2d) obj; + if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) + return false; + if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) + return false; + return true; + } + + public boolean equals(Vector2dc v, double delta) { + if (this == v) + return true; + if (v == null) + return false; + if (!(v instanceof Vector2dc)) + return false; + if (!Runtime.equals(x, v.x(), delta)) + return false; + if (!Runtime.equals(y, v.y(), delta)) + return false; + return true; + } + + public boolean equals(double x, double y) { + if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(x)) + return false; + if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(y)) + return false; + return true; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + ")"; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector2d fma(Vector2dc a, Vector2dc b) { + this.x = x + a.x() * b.x(); + this.y = y + a.y() * b.y(); + return this; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector2d fma(double a, Vector2dc b) { + this.x = x + a * b.x(); + this.y = y + a * b.y(); + return this; + } + + public Vector2d fma(Vector2dc a, Vector2dc b, Vector2d dest) { + dest.x = x + a.x() * b.x(); + dest.y = y + a.y() * b.y(); + return dest; + } + + public Vector2d fma(double a, Vector2dc b, Vector2d dest) { + dest.x = x + a * b.x(); + dest.y = y + a * b.y(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector2d min(Vector2dc v) { + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + return this; + } + + public Vector2d min(Vector2dc v, Vector2d dest) { + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector2d max(Vector2dc v) { + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + return this; + } + + public Vector2d max(Vector2dc v, Vector2d dest) { + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + return dest; + } + + public int maxComponent() { + double absX = Math.abs(x); + double absY = Math.abs(y); + if (absX >= absY) + return 0; + return 1; + } + + public int minComponent() { + double absX = Math.abs(x); + double absY = Math.abs(y); + if (absX < absY) + return 0; + return 1; + } + + /** + * Set each component of this vector to the largest (closest to positive + * infinity) {@code double} value that is less than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector2d floor() { + this.x = Math.floor(x); + this.y = Math.floor(y); + return this; + } + + public Vector2d floor(Vector2d dest) { + dest.x = Math.floor(x); + dest.y = Math.floor(y); + return dest; + } + + /** + * Set each component of this vector to the smallest (closest to negative + * infinity) {@code double} value that is greater than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector2d ceil() { + this.x = Math.ceil(x); + this.y = Math.ceil(y); + return this; + } + + public Vector2d ceil(Vector2d dest) { + dest.x = Math.ceil(x); + dest.y = Math.ceil(y); + return dest; + } + + /** + * Set each component of this vector to the closest double that is equal to + * a mathematical integer, with ties rounding to positive infinity. + * + * @return this + */ + public Vector2d round() { + this.x = Math.round(x); + this.y = Math.round(y); + return this; + } + + public Vector2d round(Vector2d dest) { + dest.x = Math.round(x); + dest.y = Math.round(y); + return dest; + } + + public boolean isFinite() { + return Math.isFinite(x) && Math.isFinite(y); + } + + /** + * Set this vector's components to their respective absolute values. + * + * @return this + */ + public Vector2d absolute() { + this.x = Math.abs(this.x); + this.y = Math.abs(this.y); + return this; + } + + public Vector2d absolute(Vector2d dest) { + dest.x = Math.abs(this.x); + dest.y = Math.abs(this.y); + return dest; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2dc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2dc.java new file mode 100644 index 000000000..eb396da52 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2dc.java @@ -0,0 +1,670 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 2-dimensional vector of double-precision floats. + * + * @author Kai Burjack + */ +public interface Vector2dc { + + /** + * @return the value of the x component + */ + double x(); + + /** + * @return the value of the y component + */ + double y(); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + * @see #get(int, ByteBuffer) + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + * @see #get(int, DoubleBuffer) + */ + DoubleBuffer get(DoubleBuffer buffer); + + /** + * Store this vector into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + */ + DoubleBuffer get(int index, DoubleBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector2dc getToAddress(long address); + + /** + * Subtract (x, y) from this vector and store the result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector2d sub(double x, double y, Vector2d dest); + + /** + * Subtract v from this vector and store the result in dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector2d sub(Vector2dc v, Vector2d dest); + + /** + * Subtract v from this vector and store the result in dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector2d sub(Vector2fc v, Vector2d dest); + + /** + * Multiply the components of this vector by the given scalar and store the result in dest. + * + * @param scalar + * the value to multiply this vector's components by + * @param dest + * will hold the result + * @return dest + */ + Vector2d mul(double scalar, Vector2d dest); + + /** + * Multiply the components of this Vector2d by the given scalar values and store the result in dest. + * + * @param x + * the x component to multiply this vector by + * @param y + * the y component to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2d mul(double x, double y, Vector2d dest); + + /** + * Multiply this Vector2d component-wise by another Vector2d and store the result in dest. + * + * @param v + * the vector to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector2d mul(Vector2dc v, Vector2d dest); + + /** + * Divide this Vector2d by the given scalar value and store the result in dest. + * + * @param scalar + * the scalar to divide this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2d div(double scalar, Vector2d dest); + + /** + * Divide the components of this Vector3f by the given scalar values and store the result in dest. + * + * @param x + * the x component to divide this vector by + * @param y + * the y component to divide this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2d div(double x, double y, Vector2d dest); + + /** + * Divide this Vector2d component-wise by another Vector2f and store the result in dest. + * + * @param v + * the vector to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector2d div(Vector2fc v, Vector2d dest); + + /** + * Divide this by v component-wise and store the result into dest. + * + * @param v + * the vector to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector2d div(Vector2dc v, Vector2d dest); + + /** + * Multiply the given matrix mat with this and store the + * result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2d mul(Matrix2dc mat, Vector2d dest); + + /** + * Multiply the given matrix mat with this and store the + * result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2d mul(Matrix2fc mat, Vector2d dest); + + /** + * Multiply the transpose of the given matrix with this Vector2f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector2d mulTranspose(Matrix2dc mat, Vector2d dest); + + /** + * Multiply the transpose of the given matrix with this Vector2f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector2d mulTranspose(Matrix2fc mat, Vector2d dest); + + /** + * Multiply the given 3x2 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the z component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2d mulPosition(Matrix3x2dc mat, Vector2d dest); + + /** + * Multiply the given 3x2 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the z component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2d mulDirection(Matrix3x2dc mat, Vector2d dest); + + /** + * Return the dot product of this vector and v. + * + * @param v + * the other vector + * @return the dot product + */ + double dot(Vector2dc v); + + /** + * Return the angle between this vector and the supplied vector. + * + * @param v + * the other vector + * @return the angle, in radians + */ + double angle(Vector2dc v); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + double lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + double length(); + + /** + * Return the distance between this and v. + * + * @param v + * the other vector + * @return the distance + */ + double distance(Vector2dc v); + + /** + * Return the distance squared between this and v. + * + * @param v + * the other vector + * @return the distance squared + */ + double distanceSquared(Vector2dc v); + + /** + * Return the distance between this and v. + * + * @param v + * the other vector + * @return the distance + */ + double distance(Vector2fc v); + + /** + * Return the distance squared between this and v. + * + * @param v + * the other vector + * @return the distance squared + */ + double distanceSquared(Vector2fc v); + + /** + * Return the distance between this vector and (x, y). + * + * @param x + * the x component of the other vector + * @param y + * the y component of the other vector + * @return the euclidean distance + */ + double distance(double x, double y); + + /** + * Return the distance squared between this vector and (x, y). + * + * @param x + * the x component of the other vector + * @param y + * the y component of the other vector + * @return the euclidean distance squared + */ + double distanceSquared(double x, double y); + + /** + * Normalize this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2d normalize(Vector2d dest); + + /** + * Scale this vector to have the given length and store the result in dest. + * + * @param length + * the desired length + * @param dest + * will hold the result + * @return dest + */ + Vector2d normalize(double length, Vector2d dest); + + /** + * Add (x, y) to this vector and store the result in dest. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param dest + * will hold the result + * @return dest + */ + Vector2d add(double x, double y, Vector2d dest); + + /** + * Add v to this vector and store the result in dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector2d add(Vector2dc v, Vector2d dest); + + /** + * Add v to this vector and store the result in dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector2d add(Vector2fc v, Vector2d dest); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2d negate(Vector2d dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Vector2d lerp(Vector2dc other, double t, Vector2d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector2d fma(Vector2dc a, Vector2dc b, Vector2d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector2d fma(double a, Vector2dc b, Vector2d dest); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector2d min(Vector2dc v, Vector2d dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector2d max(Vector2dc v, Vector2d dest); + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..1] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..1] + */ + int minComponent(); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..1] + * @return the value + * @throws IllegalArgumentException if component is not within [0..1] + */ + double get(int component) throws IllegalArgumentException; + + /** + * Set the components of the given vector dest to those of this vector + * using the given {@link RoundingMode}. + * + * @param mode + * the {@link RoundingMode} to use + * @param dest + * will hold the result + * @return dest + */ + Vector2i get(int mode, Vector2i dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2f get(Vector2f dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2d get(Vector2d dest); + + /** + * Compute for each component of this vector the largest (closest to positive + * infinity) {@code double} value that is less than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2d floor(Vector2d dest); + + /** + * Compute for each component of this vector the smallest (closest to negative + * infinity) {@code double} value that is greater than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2d ceil(Vector2d dest); + + /** + * Compute for each component of this vector the closest double that is equal to + * a mathematical integer, with ties rounding to positive infinity and store + * the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2d round(Vector2d dest); + + /** + * Determine whether all components are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + * Compute the absolute of each of this vector's components + * and store the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2d absolute(Vector2d dest); + + /** + * Compare the vector components of this vector with the given vector using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param v + * the other vector + * @param delta + * the allowed maximum difference + * @return true whether all of the vector components are equal; false otherwise + */ + boolean equals(Vector2dc v, double delta); + + /** + * Compare the vector components of this vector with the given (x, y) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @return true if all the vector components are equal + */ + boolean equals(double x, double y); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2f.java new file mode 100644 index 000000000..148f885af --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2f.java @@ -0,0 +1,1252 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Represents a 2D vector with single-precision. + * + * @author RGreenlees + * @author Kai Burjack + * @author F. Neurath + */ +public class Vector2f implements Externalizable, Cloneable, Vector2fc { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public float x; + /** + * The y component of the vector. + */ + public float y; + + /** + * Create a new {@link Vector2f} and initialize its components to zero. + */ + public Vector2f() { + } + + /** + * Create a new {@link Vector2f} and initialize both of its components with the given value. + * + * @param d + * the value of both components + */ + public Vector2f(float d) { + this.x = d; + this.y = d; + } + + /** + * Create a new {@link Vector2f} and initialize its components to the given values. + * + * @param x + * the x component + * @param y + * the y component + */ + public Vector2f(float x, float y) { + this.x = x; + this.y = y; + } + + /** + * Create a new {@link Vector2f} and initialize its components to the one of the given vector. + * + * @param v + * the {@link Vector2fc} to copy the values from + */ + public Vector2f(Vector2fc v) { + x = v.x(); + y = v.y(); + } + + /** + * Create a new {@link Vector2f} and initialize its components to the one of the given vector. + * + * @param v + * the {@link Vector2ic} to copy the values from + */ + public Vector2f(Vector2ic v) { + x = v.x(); + y = v.y(); + } + + /** + * Create a new {@link Vector2f} and initialize its two components from the first + * two elements of the given array. + * + * @param xy + * the array containing at least two elements + */ + public Vector2f(float[] xy) { + this.x = xy[0]; + this.y = xy[1]; + } + + /** + * Create a new {@link Vector2f} and read this vector from the supplied {@link ByteBuffer} + * at the current buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #Vector2f(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y order + * @see #Vector2f(int, ByteBuffer) + */ + public Vector2f(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector2f} and read this vector from the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer values will be read in x, y order + */ + public Vector2f(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector2f} and read this vector from the supplied {@link FloatBuffer} + * at the current buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is read, use {@link #Vector2f(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y order + * @see #Vector2f(int, FloatBuffer) + */ + public Vector2f(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector2f} and read this vector from the supplied {@link FloatBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * values will be read in x, y order + */ + public Vector2f(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public float x() { + return this.x; + } + + public float y() { + return this.y; + } + + /** + * Set the x and y components to the supplied value. + * + * @param d + * the value of both components + * @return this + */ + public Vector2f set(float d) { + this.x = d; + this.y = d; + return this; + } + + /** + * Set the x and y components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @return this + */ + public Vector2f set(float x, float y) { + this.x = x; + this.y = y; + return this; + } + + /** + * Set the x and y components to the supplied value. + * + * @param d + * the value of both components + * @return this + */ + public Vector2f set(double d) { + this.x = (float) d; + this.y = (float) d; + return this; + } + + /** + * Set the x and y components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @return this + */ + public Vector2f set(double x, double y) { + this.x = (float) x; + this.y = (float) y; + return this; + } + + /** + * Set this {@link Vector2f} to the values of v. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector2f set(Vector2fc v) { + this.x = v.x(); + this.y = v.y(); + return this; + } + + /** + * Set this {@link Vector2f} to the values of v. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector2f set(Vector2ic v) { + this.x = v.x(); + this.y = v.y(); + return this; + } + + /** + * Set this {@link Vector2f} to the values of v. + *

+ * Note that due to the given vector v storing the components in double-precision, + * there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector2f set(Vector2dc v) { + this.x = (float) v.x(); + this.y = (float) v.y(); + return this; + } + + /** + * Set the two components of this vector to the first two elements of the given array. + * + * @param xy + * the array containing at least two elements + * @return this + */ + public Vector2f set(float[] xy) { + this.x = xy[0]; + this.y = xy[1]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #set(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y order + * @return this + * @see #set(int, ByteBuffer) + */ + public Vector2f set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y order + * @return this + */ + public Vector2f set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is read, use {@link #set(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y order + * @return this + * @see #set(int, FloatBuffer) + */ + public Vector2f set(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * values will be read in x, y order + * @return this + */ + public Vector2f set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 2 float values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector2f setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + public float get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + default: + throw new IllegalArgumentException(); + } + } + + public Vector2i get(int mode, Vector2i dest) { + dest.x = Math.roundUsing(this.x(), mode); + dest.y = Math.roundUsing(this.y(), mode); + return dest; + } + + public Vector2f get(Vector2f dest) { + dest.x = this.x(); + dest.y = this.y(); + return dest; + } + + public Vector2d get(Vector2d dest) { + dest.x = this.x(); + dest.y = this.y(); + return dest; + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..1] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..1] + */ + public Vector2f setComponent(int component, float value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public FloatBuffer get(FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector2fc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Set this vector to be one of its perpendicular vectors. + * + * @return this + */ + public Vector2f perpendicular() { + float xTemp = y; + this.y = this.x * -1; + this.x = xTemp; + return this; + } + + /** + * Subtract v from this vector. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector2f sub(Vector2fc v) { + this.x = x - v.x(); + this.y = y - v.y(); + return this; + } + + public Vector2f sub(Vector2fc v, Vector2f dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + return dest; + } + + /** + * Subtract (x, y) from this vector. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @return this + */ + public Vector2f sub(float x, float y) { + this.x = this.x - x; + this.y = this.y - y; + return this; + } + + public Vector2f sub(float x, float y, Vector2f dest) { + dest.x = this.x - x; + dest.y = this.y - y; + return dest; + } + + public float dot(Vector2fc v) { + return x * v.x() + y * v.y(); + } + + public float angle(Vector2fc v) { + float dot = x*v.x() + y*v.y(); + float det = x*v.y() - y*v.x(); + return Math.atan2(det, dot); + } + + public float lengthSquared() { + return x * x + y * y; + } + + /** + * Get the length squared of a 2-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * + * @return the length squared of the given vector + * + * @author F. Neurath + */ + public static float lengthSquared(float x, float y) { + return x * x + y * y; + } + + public float length() { + return Math.sqrt(x * x + y * y); + } + + /** + * Get the length of a 2-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * + * @return the length of the given vector + * + * @author F. Neurath + */ + public static float length(float x, float y) { + return Math.sqrt(x * x + y * y); + } + + public float distance(Vector2fc v) { + float dx = this.x - v.x(); + float dy = this.y - v.y(); + return Math.sqrt(dx * dx + dy * dy); + } + + public float distanceSquared(Vector2fc v) { + float dx = this.x - v.x(); + float dy = this.y - v.y(); + return dx * dx + dy * dy; + } + + public float distance(float x, float y) { + float dx = this.x - x; + float dy = this.y - y; + return Math.sqrt(dx * dx + dy * dy); + } + + public float distanceSquared(float x, float y) { + float dx = this.x - x; + float dy = this.y - y; + return dx * dx + dy * dy; + } + + /** + * Return the distance between (x1, y1) and (x2, y2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @return the euclidean distance + */ + public static float distance(float x1, float y1, float x2, float y2) { + float dx = x1 - x2; + float dy = y1 - y2; + return Math.sqrt(dx * dx + dy * dy); + } + + /** + * Return the squared distance between (x1, y1) and (x2, y2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @return the euclidean distance squared + */ + public static float distanceSquared(float x1, float y1, float x2, float y2) { + float dx = x1 - x2; + float dy = y1 - y2; + return dx * dx + dy * dy; + } + + /** + * Normalize this vector. + * + * @return this + */ + public Vector2f normalize() { + float invLength = Math.invsqrt(x * x + y * y); + this.x = x * invLength; + this.y = y * invLength; + return this; + } + + public Vector2f normalize(Vector2f dest) { + float invLength = Math.invsqrt(x * x + y * y); + dest.x = x * invLength; + dest.y = y * invLength; + return dest; + } + + /** + * Scale this vector to have the given length. + * + * @param length + * the desired length + * @return this + */ + public Vector2f normalize(float length) { + float invLength = Math.invsqrt(x * x + y * y) * length; + this.x = x * invLength; + this.y = y * invLength; + return this; + } + + public Vector2f normalize(float length, Vector2f dest) { + float invLength = Math.invsqrt(x * x + y * y) * length; + dest.x = x * invLength; + dest.y = y * invLength; + return dest; + } + + /** + * Add v to this vector. + * + * @param v + * the vector to add + * @return this + */ + public Vector2f add(Vector2fc v) { + this.x = x + v.x(); + this.y = y + v.y(); + return this; + } + + public Vector2f add(Vector2fc v, Vector2f dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + return dest; + } + + /** + * Increment the components of this vector by the given values. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @return this + */ + public Vector2f add(float x, float y) { + return add(x, y, this); + } + + public Vector2f add(float x, float y, Vector2f dest) { + dest.x = this.x + x; + dest.y = this.y + y; + return dest; + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector2f zero() { + this.x = 0; + this.y = 0; + return this; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(x); + out.writeFloat(y); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + x = in.readFloat(); + y = in.readFloat(); + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector2f negate() { + this.x = -x; + this.y = -y; + return this; + } + + public Vector2f negate(Vector2f dest) { + dest.x = -x; + dest.y = -y; + return dest; + } + + /** + * Multiply the components of this vector by the given scalar. + * + * @param scalar + * the value to multiply this vector's components by + * @return this + */ + public Vector2f mul(float scalar) { + this.x = x * scalar; + this.y = y * scalar; + return this; + } + + public Vector2f mul(float scalar, Vector2f dest) { + dest.x = x * scalar; + dest.y = y * scalar; + return dest; + } + + /** + * Multiply the components of this Vector2f by the given scalar values and store the result in this. + * + * @param x + * the x component to multiply this vector by + * @param y + * the y component to multiply this vector by + * @return this + */ + public Vector2f mul(float x, float y) { + this.x = this.x * x; + this.y = this.y * y; + return this; + } + + public Vector2f mul(float x, float y, Vector2f dest) { + dest.x = this.x * x; + dest.y = this.y * y; + return dest; + } + + /** + * Multiply this Vector2f component-wise by another Vector2f. + * + * @param v + * the vector to multiply by + * @return this + */ + public Vector2f mul(Vector2fc v) { + this.x = x * v.x(); + this.y = y * v.y(); + return this; + } + + public Vector2f mul(Vector2fc v, Vector2f dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + return dest; + } + + /** + * Divide this Vector2f component-wise by another Vector2fc. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector2f div(Vector2fc v) { + this.x = this.x / v.x(); + this.y = this.y / v.y(); + return this; + } + + public Vector2f div(Vector2fc v, Vector2f dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + return dest; + } + + /** + * Divide all components of this {@link Vector2f} by the given scalar + * value. + * + * @param scalar + * the scalar to divide by + * @return this + */ + public Vector2f div(float scalar) { + float inv = 1.0f / scalar; + this.x = this.x * inv; + this.y = this.y * inv; + return this; + } + + public Vector2f div(float scalar, Vector2f dest) { + float inv = 1.0f / scalar; + dest.x = this.x * inv; + dest.y = this.y * inv; + return dest; + } + + /** + * Divide the components of this Vector2f by the given scalar values and store the result in this. + * + * @param x + * the x component to divide this vector by + * @param y + * the y component to divide this vector by + * @return this + */ + public Vector2f div(float x, float y) { + this.x = this.x / x; + this.y = this.y / y; + return this; + } + + public Vector2f div(float x, float y, Vector2f dest) { + dest.x = this.x / x; + dest.y = this.y / y; + return dest; + } + + /** + * Multiply the given matrix with this Vector2f and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector2f mul(Matrix2fc mat) { + float rx = mat.m00() * x + mat.m10() * y; + float ry = mat.m01() * x + mat.m11() * y; + this.x = rx; + this.y = ry; + return this; + } + + public Vector2f mul(Matrix2fc mat, Vector2f dest) { + float rx = mat.m00() * x + mat.m10() * y; + float ry = mat.m01() * x + mat.m11() * y; + dest.x = rx; + dest.y = ry; + return dest; + } + + /** + * Multiply the given matrix with this Vector2f and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector2f mul(Matrix2dc mat) { + double rx = mat.m00() * x + mat.m10() * y; + double ry = mat.m01() * x + mat.m11() * y; + this.x = (float) rx; + this.y = (float) ry; + return this; + } + + public Vector2f mul(Matrix2dc mat, Vector2f dest) { + double rx = mat.m00() * x + mat.m10() * y; + double ry = mat.m01() * x + mat.m11() * y; + dest.x = (float) rx; + dest.y = (float) ry; + return dest; + } + + /** + * Multiply the transpose of the given matrix with this Vector2f store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector2f mulTranspose(Matrix2fc mat) { + float rx = mat.m00() * x + mat.m01() * y; + float ry = mat.m10() * x + mat.m11() * y; + this.x = rx; + this.y = ry; + return this; + } + + public Vector2f mulTranspose(Matrix2fc mat, Vector2f dest) { + float rx = mat.m00() * x + mat.m01() * y; + float ry = mat.m10() * x + mat.m11() * y; + dest.x = rx; + dest.y = ry; + return dest; + } + + /** + * Multiply the given 3x2 matrix mat with this. + *

+ * This method assumes the z component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector2f mulPosition(Matrix3x2fc mat) { + this.x = mat.m00() * x + mat.m10() * y + mat.m20(); + this.y = mat.m01() * x + mat.m11() * y + mat.m21(); + return this; + } + + public Vector2f mulPosition(Matrix3x2fc mat, Vector2f dest) { + dest.x = mat.m00() * x + mat.m10() * y + mat.m20(); + dest.y = mat.m01() * x + mat.m11() * y + mat.m21(); + return dest; + } + + /** + * Multiply the given 3x2 matrix mat with this. + *

+ * This method assumes the z component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector2f mulDirection(Matrix3x2fc mat) { + this.x = mat.m00() * x + mat.m10() * y; + this.y = mat.m01() * x + mat.m11() * y; + return this; + } + + public Vector2f mulDirection(Matrix3x2fc mat, Vector2f dest) { + dest.x = mat.m00() * x + mat.m10() * y; + dest.y = mat.m01() * x + mat.m11() * y; + return dest; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Vector2f lerp(Vector2fc other, float t) { + this.x = x + (other.x() - x) * t; + this.y = y + (other.y() - y) * t; + return this; + } + + public Vector2f lerp(Vector2fc other, float t, Vector2f dest) { + dest.x = x + (other.x() - x) * t; + dest.y = y + (other.y() - y) * t; + return dest; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(x); + result = prime * result + Float.floatToIntBits(y); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector2f other = (Vector2f) obj; + if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) + return false; + if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) + return false; + return true; + } + + public boolean equals(Vector2fc v, float delta) { + if (this == v) + return true; + if (v == null) + return false; + if (!(v instanceof Vector2fc)) + return false; + if (!Runtime.equals(x, v.x(), delta)) + return false; + if (!Runtime.equals(y, v.y(), delta)) + return false; + return true; + } + + public boolean equals(float x, float y) { + if (Float.floatToIntBits(this.x) != Float.floatToIntBits(x)) + return false; + if (Float.floatToIntBits(this.y) != Float.floatToIntBits(y)) + return false; + return true; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + ")"; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector2f fma(Vector2fc a, Vector2fc b) { + this.x = x + a.x() * b.x(); + this.y = y + a.y() * b.y(); + return this; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector2f fma(float a, Vector2fc b) { + this.x = x + a * b.x(); + this.y = y + a * b.y(); + return this; + } + + public Vector2f fma(Vector2fc a, Vector2fc b, Vector2f dest) { + dest.x = x + a.x() * b.x(); + dest.y = y + a.y() * b.y(); + return dest; + } + + public Vector2f fma(float a, Vector2fc b, Vector2f dest) { + dest.x = x + a * b.x(); + dest.y = y + a * b.y(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector2f min(Vector2fc v) { + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + return this; + } + + public Vector2f min(Vector2fc v, Vector2f dest) { + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector2f max(Vector2fc v) { + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + return this; + } + + public Vector2f max(Vector2fc v, Vector2f dest) { + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + return dest; + } + + public int maxComponent() { + float absX = Math.abs(x); + float absY = Math.abs(y); + if (absX >= absY) + return 0; + return 1; + } + + public int minComponent() { + float absX = Math.abs(x); + float absY = Math.abs(y); + if (absX < absY) + return 0; + return 1; + } + + /** + * Set each component of this vector to the largest (closest to positive + * infinity) {@code float} value that is less than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector2f floor() { + this.x = Math.floor(x); + this.y = Math.floor(y); + return this; + } + + public Vector2f floor(Vector2f dest) { + dest.x = Math.floor(x); + dest.y = Math.floor(y); + return dest; + } + + /** + * Ceil each component of this vector + * + * @return this + */ + public Vector2f ceil() { + this.x = Math.ceil(x); + this.y = Math.ceil(y); + return this; + } + + public Vector2f ceil(Vector2f dest) { + dest.x = Math.ceil(x); + dest.y = Math.ceil(y); + return dest; + } + + /** + * Set each component of this vector to the closest float that is equal to + * a mathematical integer, with ties rounding to positive infinity. + * + * @return this + */ + public Vector2f round() { + this.x = Math.ceil(x); + this.y = Math.ceil(y); + return this; + } + + public Vector2f round(Vector2f dest) { + dest.x = Math.round(x); + dest.y = Math.round(y); + return dest; + } + + public boolean isFinite() { + return Math.isFinite(x) && Math.isFinite(y); + } + + /** + * Set this vector's components to their respective absolute values. + * + * @return this + */ + public Vector2f absolute() { + this.x = Math.abs(this.x); + this.y = Math.abs(this.y); + return this; + } + + public Vector2f absolute(Vector2f dest) { + dest.x = Math.abs(this.x); + dest.y = Math.abs(this.y); + return dest; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2fc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2fc.java new file mode 100644 index 000000000..c106ddb4f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2fc.java @@ -0,0 +1,609 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 2-dimensional vector of single-precision floats. + * + * @author Kai Burjack + */ +public interface Vector2fc { + + /** + * @return the value of the x component + */ + float x(); + + /** + * @return the value of the y component + */ + float y(); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + * @see #get(int, ByteBuffer) + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + * @see #get(int, FloatBuffer) + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector2fc getToAddress(long address); + + /** + * Subtract v from this vector and store the result in dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector2f sub(Vector2fc v, Vector2f dest); + + /** + * Subtract (x, y) from this vector and store the result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector2f sub(float x, float y, Vector2f dest); + + /** + * Return the dot product of this vector and v. + * + * @param v + * the other vector + * @return the dot product + */ + float dot(Vector2fc v); + + /** + * Return the angle between this vector and the supplied vector. + * + * @param v + * the other vector + * @return the angle, in radians + */ + float angle(Vector2fc v); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + float lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + float length(); + + /** + * Return the distance between this and v. + * + * @param v + * the other vector + * @return the distance + */ + float distance(Vector2fc v); + + /** + * Return the distance squared between this and v. + * + * @param v + * the other vector + * @return the distance squared + */ + float distanceSquared(Vector2fc v); + + /** + * Return the distance between this vector and (x, y). + * + * @param x + * the x component of the other vector + * @param y + * the y component of the other vector + * @return the euclidean distance + */ + float distance(float x, float y); + + /** + * Return the distance squared between this vector and (x, y). + * + * @param x + * the x component of the other vector + * @param y + * the y component of the other vector + * @return the euclidean distance squared + */ + float distanceSquared(float x, float y); + + /** + * Normalize this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2f normalize(Vector2f dest); + + /** + * Scale this vector to have the given length and store the result in dest. + * + * @param length + * the desired length + * @param dest + * will hold the result + * @return dest + */ + Vector2f normalize(float length, Vector2f dest); + + /** + * Add the supplied vector to this one and store the result in + * dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector2f add(Vector2fc v, Vector2f dest); + + /** + * Increment the components of this vector by the given values and store the result in dest. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param dest + * will hold the result + * @return dest + */ + Vector2f add(float x, float y, Vector2f dest); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2f negate(Vector2f dest); + + /** + * Multiply the components of this vector by the given scalar and store the result in dest. + * + * @param scalar + * the value to multiply this vector's components by + * @param dest + * will hold the result + * @return dest + */ + Vector2f mul(float scalar, Vector2f dest); + + /** + * Multiply the components of this Vector2f by the given scalar values and store the result in dest. + * + * @param x + * the x component to multiply this vector by + * @param y + * the y component to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2f mul(float x, float y, Vector2f dest); + + /** + * Multiply this Vector2f component-wise by another Vector2f and store the result in dest. + * + * @param v + * the vector to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector2f mul(Vector2fc v, Vector2f dest); + + /** + * Divide all components of this {@link Vector2f} by the given scalar + * value and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector2f div(float scalar, Vector2f dest); + + /** + * Divide this Vector2f component-wise by another Vector2fc + * and store the result in dest. + * + * @param v + * the vector to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector2f div(Vector2fc v, Vector2f dest); + + /** + * Divide the components of this Vector2f by the given scalar values and store the result in dest. + * + * @param x + * the x component to divide this vector by + * @param y + * the y component to divide this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2f div(float x, float y, Vector2f dest); + + /** + * Multiply the given matrix with this Vector2f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector2f mul(Matrix2fc mat, Vector2f dest); + + /** + * Multiply the given matrix with this Vector2f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector2f mul(Matrix2dc mat, Vector2f dest); + + /** + * Multiply the transpose of the given matrix with this Vector3f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector2f mulTranspose(Matrix2fc mat, Vector2f dest); + + /** + * Multiply the given 3x2 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the z component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2f mulPosition(Matrix3x2fc mat, Vector2f dest); + + /** + * Multiply the given 3x2 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the z component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2f mulDirection(Matrix3x2fc mat, Vector2f dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Vector2f lerp(Vector2fc other, float t, Vector2f dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector2f fma(Vector2fc a, Vector2fc b, Vector2f dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector2f fma(float a, Vector2fc b, Vector2f dest); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector2f min(Vector2fc v, Vector2f dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector2f max(Vector2fc v, Vector2f dest); + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..1] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..1] + */ + int minComponent(); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..1] + * @return the value + * @throws IllegalArgumentException if component is not within [0..1] + */ + float get(int component) throws IllegalArgumentException; + + /** + * Set the components of the given vector dest to those of this vector + * using the given {@link RoundingMode}. + * + * @param mode + * the {@link RoundingMode} to use + * @param dest + * will hold the result + * @return dest + */ + Vector2i get(int mode, Vector2i dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2f get(Vector2f dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2d get(Vector2d dest); + + /** + * Compute for each component of this vector the largest (closest to positive + * infinity) {@code float} value that is less than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2f floor(Vector2f dest); + + /** + * Compute for each component of this vector the smallest (closest to negative + * infinity) {@code float} value that is greater than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2f ceil(Vector2f dest); + + /** + * Compute for each component of this vector the closest float that is equal to + * a mathematical integer, with ties rounding to positive infinity and store + * the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2f round(Vector2f dest); + + /** + * Determine whether all components are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + * Compute the absolute of each of this vector's components + * and store the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2f absolute(Vector2f dest); + + /** + * Compare the vector components of this vector with the given vector using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param v + * the other vector + * @param delta + * the allowed maximum difference + * @return true whether all of the vector components are equal; false otherwise + */ + boolean equals(Vector2fc v, float delta); + + /** + * Compare the vector components of this vector with the given (x, y) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @return true if all the vector components are equal + */ + boolean equals(float x, float y); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2i.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2i.java new file mode 100644 index 000000000..146ffd7e2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2i.java @@ -0,0 +1,965 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Represents a 2D vector with single-precision. + * + * @author RGreenlees + * @author Kai Burjack + * @author Hans Uhlig + */ +public class Vector2i implements Externalizable, Cloneable, Vector2ic { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public int x; + /** + * The y component of the vector. + */ + public int y; + + /** + * Create a new {@link Vector2i} and initialize its components to zero. + */ + public Vector2i() { + } + + /** + * Create a new {@link Vector2i} and initialize both of its components with + * the given value. + * + * @param s + * the value of both components + */ + public Vector2i(int s) { + this.x = s; + this.y = s; + } + + /** + * Create a new {@link Vector2i} and initialize its components to the given values. + * + * @param x + * the x component + * @param y + * the y component + */ + public Vector2i(int x, int y) { + this.x = x; + this.y = y; + } + + /** + * Create a new {@link Vector2i} and initialize its component values and + * round using the given {@link RoundingMode}. + * @param x + * the x component + * @param y + * the y component + * @param mode + * the {@link RoundingMode} to use + */ + public Vector2i(float x, float y, int mode) { + this.x = Math.roundUsing(x, mode); + this.y = Math.roundUsing(y, mode); + } + + /** + * Create a new {@link Vector2i} and initialize its component values and + * round using the given {@link RoundingMode}. + * @param x + * the x component + * @param y + * the y component + * @param mode + * the {@link RoundingMode} to use + */ + public Vector2i(double x, double y, int mode) { + this.x = Math.roundUsing(x, mode); + this.y = Math.roundUsing(y, mode); + } + + /** + * Create a new {@link Vector2i} and initialize its components to the one of + * the given vector. + * + * @param v + * the {@link Vector2ic} to copy the values from + */ + public Vector2i(Vector2ic v) { + x = v.x(); + y = v.y(); + } + + /** + * Create a new {@link Vector2i} and initialize its components to the rounded value of + * the given vector. + * + * @param v + * the {@link Vector2fc} to round and copy the values from + * @param mode + * the {@link RoundingMode} to use + */ + public Vector2i(Vector2fc v, int mode) { + x = Math.roundUsing(v.x(), mode); + y = Math.roundUsing(v.y(), mode); + } + + /** + * Create a new {@link Vector2i} and initialize its components to the rounded value of + * the given vector. + * + * @param v + * the {@link Vector2dc} to round and copy the values from + * @param mode + * the {@link RoundingMode} to use + */ + public Vector2i(Vector2dc v, int mode) { + x = Math.roundUsing(v.x(), mode); + y = Math.roundUsing(v.y(), mode); + } + + /** + * Create a new {@link Vector2i} and initialize its two components from the first + * two elements of the given array. + * + * @param xy + * the array containing at least three elements + */ + public Vector2i(int[] xy) { + this.x = xy[0]; + this.y = xy[1]; + } + + /** + * Create a new {@link Vector2i} and read this vector from the supplied + * {@link ByteBuffer} at the current buffer + * {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * read, use {@link #Vector2i(int, ByteBuffer)}, taking the absolute + * position as parameter. + * + * @see #Vector2i(int, ByteBuffer) + * + * @param buffer + * values will be read in x, y order + */ + public Vector2i(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector2i} and read this vector from the supplied + * {@link ByteBuffer} starting at the specified absolute buffer + * position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y order + */ + public Vector2i(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector2i} and read this vector from the supplied + * {@link IntBuffer} at the current buffer + * {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * read, use {@link #Vector2i(int, IntBuffer)}, taking the absolute position + * as parameter. + * + * @see #Vector2i(int, IntBuffer) + * + * @param buffer + * values will be read in x, y order + */ + public Vector2i(IntBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector2i} and read this vector from the supplied + * {@link IntBuffer} starting at the specified absolute buffer + * position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param buffer + * values will be read in x, y order + */ + public Vector2i(int index, IntBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public int x() { + return this.x; + } + + public int y() { + return this.y; + } + + /** + * Set the x and y components to the supplied value. + * + * @param s + * scalar value of both components + * @return this + */ + public Vector2i set(int s) { + this.x = s; + this.y = s; + return this; + } + + /** + * Set the x and y components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @return this + */ + public Vector2i set(int x, int y) { + this.x = x; + this.y = y; + return this; + } + + /** + * Set this {@link Vector2i} to the values of v. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector2i set(Vector2ic v) { + this.x = v.x(); + this.y = v.y(); + return this; + } + + /** + * Set this {@link Vector2i} to the values of v using {@link RoundingMode#TRUNCATE} rounding. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector2i set(Vector2dc v) { + this.x = (int) v.x(); + this.y = (int) v.y(); + return this; + } + + /** + * Set this {@link Vector2i} to the values of v using the given {@link RoundingMode}. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @param mode + * the {@link RoundingMode} to use + * @return this + */ + public Vector2i set(Vector2dc v, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + return this; + } + + /** + * Set this {@link Vector2i} to the values of v using the given {@link RoundingMode}. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @param mode + * the {@link RoundingMode} to use + * @return this + */ + public Vector2i set(Vector2fc v, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + return this; + } + + /** + * Set the two components of this vector to the first two elements of the given array. + * + * @param xy + * the array containing at least two elements + * @return this + */ + public Vector2i set(int[] xy) { + this.x = xy[0]; + this.y = xy[1]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * read, use {@link #set(int, ByteBuffer)}, taking the absolute position as + * parameter. + * + * @see #set(int, ByteBuffer) + * + * @param buffer + * values will be read in x, y order + * @return this + */ + public Vector2i set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y order + * @return this + */ + public Vector2i set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link IntBuffer} at the current + * buffer {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * read, use {@link #set(int, IntBuffer)}, taking the absolute position as + * parameter. + * + * @see #set(int, IntBuffer) + * + * @param buffer + * values will be read in x, y order + * @return this + */ + public Vector2i set(IntBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link IntBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param buffer + * values will be read in x, y order + * @return this + */ + public Vector2i set(int index, IntBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 2 integer values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector2i setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + public int get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + default: + throw new IllegalArgumentException(); + } + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..1] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..1] + */ + public Vector2i setComponent(int component, int value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public IntBuffer get(IntBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public IntBuffer get(int index, IntBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector2ic getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Subtract the supplied vector from this one and store the result in + * this. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector2i sub(Vector2ic v) { + this.x = x - v.x(); + this.y = y - v.y(); + return this; + } + + public Vector2i sub(Vector2ic v, Vector2i dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + return dest; + } + + /** + * Decrement the components of this vector by the given values. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @return this + */ + public Vector2i sub(int x, int y) { + this.x = this.x - x; + this.y = this.y - y; + return this; + } + + public Vector2i sub(int x, int y, Vector2i dest) { + dest.x = this.x - x; + dest.y = this.y - y; + return dest; + } + + public long lengthSquared() { + return x * x + y * y; + } + + /** + * Get the length squared of a 2-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * + * @return the length squared of the given vector + */ + public static long lengthSquared(int x, int y) { + return x * x + y * y; + } + + public double length() { + return Math.sqrt(x * x + y * y); + } + + /** + * Get the length of a 2-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * + * @return the length squared of the given vector + */ + public static double length(int x, int y) { + return Math.sqrt(x * x + y * y); + } + + public double distance(Vector2ic v) { + int dx = this.x - v.x(); + int dy = this.y - v.y(); + return Math.sqrt(dx * dx + dy * dy); + } + + public double distance(int x, int y) { + int dx = this.x - x; + int dy = this.y - y; + return Math.sqrt(dx * dx + dy * dy); + } + + public long distanceSquared(Vector2ic v) { + int dx = this.x - v.x(); + int dy = this.y - v.y(); + return dx * dx + dy * dy; + } + + public long distanceSquared(int x, int y) { + int dx = this.x - x; + int dy = this.y - y; + return dx * dx + dy * dy; + } + + public long gridDistance(Vector2ic v) { + return Math.abs(v.x() - x()) + Math.abs(v.y() - y()); + } + + public long gridDistance(int x, int y) { + return Math.abs(x - x()) + Math.abs(y - y()); + } + + /** + * Return the distance between (x1, y1) and (x2, y2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @return the euclidean distance + */ + public static double distance(int x1, int y1, int x2, int y2) { + int dx = x1 - x2; + int dy = y1 - y2; + return Math.sqrt(dx * dx + dy * dy); + } + + /** + * Return the squared distance between (x1, y1) and (x2, y2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @return the euclidean distance squared + */ + public static long distanceSquared(int x1, int y1, int x2, int y2) { + int dx = x1 - x2; + int dy = y1 - y2; + return dx * dx + dy * dy; + } + + /** + * Add v to this vector. + * + * @param v + * the vector to add + * @return this + */ + public Vector2i add(Vector2ic v) { + this.x = x + v.x(); + this.y = y + v.y(); + return this; + } + + public Vector2i add(Vector2ic v, Vector2i dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + return dest; + } + + /** + * Increment the components of this vector by the given values. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @return this + */ + public Vector2i add(int x, int y) { + this.x = this.x + x; + this.y = this.y + y; + return this; + } + + public Vector2i add(int x, int y, Vector2i dest) { + dest.x = this.x + x; + dest.y = this.y + y; + return dest; + } + + /** + * Multiply all components of this {@link Vector2i} by the given scalar + * value. + * + * @param scalar + * the scalar to multiply this vector by + * @return this + */ + public Vector2i mul(int scalar) { + this.x = x * scalar; + this.y = y * scalar; + return this; + } + + public Vector2i mul(int scalar, Vector2i dest) { + dest.x = x * scalar; + dest.y = y * scalar; + return dest; + } + + /** + * Add the supplied vector by this one. + * + * @param v + * the vector to multiply + * @return this + */ + public Vector2i mul(Vector2ic v) { + this.x = x * v.x(); + this.y = y * v.y(); + return this; + } + + public Vector2i mul(Vector2ic v, Vector2i dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + return dest; + } + + /** + * Multiply the components of this vector by the given values. + * + * @param x + * the x component to multiply + * @param y + * the y component to multiply + * @return this + */ + public Vector2i mul(int x, int y) { + this.x = this.x * x; + this.y = this.y * y; + return this; + } + + public Vector2i mul(int x, int y, Vector2i dest) { + dest.x = this.x * x; + dest.y = this.y * y; + return dest; + } + + /** + * Divide all components of this {@link Vector2i} by the given scalar value. + * + * @param scalar + * the scalar to divide by + * @return a vector holding the result + */ + public Vector2i div(float scalar) { + float invscalar = 1.0f / scalar; + this.x = (int) (x * invscalar); + this.y = (int) (y * invscalar); + return this; + } + + public Vector2i div(float scalar, Vector2i dest) { + float invscalar = 1.0f / scalar; + dest.x = (int) (x * invscalar); + dest.y = (int) (y * invscalar); + return dest; + } + + /** + * Divide all components of this {@link Vector2i} by the given scalar value. + * + * @param scalar + * the scalar to divide by + * @return a vector holding the result + */ + public Vector2i div(int scalar) { + this.x = x / scalar; + this.y = y / scalar; + return this; + } + + public Vector2i div(int scalar, Vector2i dest) { + dest.x = x / scalar; + dest.y = y / scalar; + return dest; + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector2i zero() { + this.x = 0; + this.y = 0; + return this; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(x); + out.writeInt(y); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + x = in.readInt(); + y = in.readInt(); + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector2i negate() { + this.x = -x; + this.y = -y; + return this; + } + + public Vector2i negate(Vector2i dest) { + dest.x = -x; + dest.y = -y; + return dest; + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector2i min(Vector2ic v) { + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + return this; + } + + public Vector2i min(Vector2ic v, Vector2i dest) { + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector2i max(Vector2ic v) { + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + return this; + } + + public Vector2i max(Vector2ic v, Vector2i dest) { + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + return dest; + } + + public int maxComponent() { + int absX = Math.abs(x); + int absY = Math.abs(y); + if (absX >= absY) + return 0; + return 1; + } + + public int minComponent() { + int absX = Math.abs(x); + int absY = Math.abs(y); + if (absX < absY) + return 0; + return 1; + } + + /** + * Set this vector's components to their respective absolute values. + * + * @return this + */ + public Vector2i absolute() { + this.x = Math.abs(this.x); + this.y = Math.abs(this.y); + return this; + } + + public Vector2i absolute(Vector2i dest) { + dest.x = Math.abs(this.x); + dest.y = Math.abs(this.y); + return dest; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + x; + result = prime * result + y; + return result; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Vector2i other = (Vector2i) obj; + if (x != other.x) { + return false; + } + if (y != other.y) { + return false; + } + return true; + } + + public boolean equals(int x, int y) { + if (this.x != x) + return false; + if (this.y != y) + return false; + return true; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + formatter.format(x) + " " + formatter.format(y) + ")"; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2ic.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2ic.java new file mode 100644 index 000000000..949e3f698 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector2ic.java @@ -0,0 +1,391 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +/** + * Interface to a read-only view of a 2-dimensional vector of integers. + * + * @author Kai Burjack + */ +public interface Vector2ic { + + /** + * @return the value of the x component + */ + int x(); + + /** + * @return the value of the y component + */ + int y(); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * stored, use {@link #get(int, ByteBuffer)}, taking the absolute position + * as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link IntBuffer} at the current + * buffer {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * stored, use {@link #get(int, IntBuffer)}, taking the absolute position as + * parameter. + * + * @see #get(int, IntBuffer) + * + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + */ + IntBuffer get(IntBuffer buffer); + + /** + * Store this vector into the supplied {@link IntBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param buffer + * will receive the values of this vector in x, y order + * @return the passed in buffer + */ + IntBuffer get(int index, IntBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector2ic getToAddress(long address); + + /** + * Subtract the supplied vector from this one and store the result in + * dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector2i sub(Vector2ic v, Vector2i dest); + + /** + * Decrement the components of this vector by the given values and store the + * result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector2i sub(int x, int y, Vector2i dest); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + long lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + double length(); + + /** + * Return the distance between this Vector and v. + * + * @param v + * the other vector + * @return the distance + */ + double distance(Vector2ic v); + + /** + * Return the distance between this vector and (x, y). + * + * @param x + * the x component of the other vector + * @param y + * the y component of the other vector + * @return the euclidean distance + */ + double distance(int x, int y); + + /** + * Return the square of the distance between this vector and v. + * + * @param v + * the other vector + * @return the squared of the distance + */ + long distanceSquared(Vector2ic v); + + /** + * Return the square of the distance between this vector and + * (x, y). + * + * @param x + * the x component of the other vector + * @param y + * the y component of the other vector + * @return the square of the distance + */ + long distanceSquared(int x, int y); + + /** + * Return the grid distance in between (aka 1-Norm, Minkowski or Manhattan distance) + * (x, y). + * + * @param v + * the other vector + * @return the grid distance + */ + long gridDistance(Vector2ic v); + + /** + * Return the grid distance in between (aka 1-Norm, Minkowski or Manhattan distance) + * (x, y). + * + * @param x + * the x component of the other vector + * @param y + * the y component of the other vector + * @return the grid distance + */ + long gridDistance(int x, int y); + + /** + * Add the supplied vector to this one and store the result in + * dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector2i add(Vector2ic v, Vector2i dest); + + /** + * Increment the components of this vector by the given values and store the + * result in dest. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param dest + * will hold the result + * @return dest + */ + Vector2i add(int x, int y, Vector2i dest); + + /** + * Multiply all components of this {@link Vector2ic} by the given scalar + * value and store the result in dest. + * + * @param scalar + * the scalar to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector2i mul(int scalar, Vector2i dest); + + /** + * Multiply the supplied vector by this one and store the result in + * dest. + * + * @param v + * the vector to multiply + * @param dest + * will hold the result + * @return dest + */ + Vector2i mul(Vector2ic v, Vector2i dest); + + /** + * Multiply the components of this vector by the given values and store the + * result in dest. + * + * @param x + * the x component to multiply + * @param y + * the y component to multiply + * @param dest + * will hold the result + * @return dest + */ + Vector2i mul(int x, int y, Vector2i dest); + + /** + * Divide all components of this {@link Vector2i} by the given scalar value + * and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector2i div(float scalar, Vector2i dest); + + /** + * Divide all components of this {@link Vector2i} by the given scalar value + * and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector2i div(int scalar, Vector2i dest); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2i negate(Vector2i dest); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector2i min(Vector2ic v, Vector2i dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector2i max(Vector2ic v, Vector2i dest); + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..1] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..1] + */ + int minComponent(); + + /** + * Compute the absolute of each of this vector's components + * and store the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector2i absolute(Vector2i dest); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..1] + * @return the value + * @throws IllegalArgumentException if component is not within [0..1] + */ + int get(int component) throws IllegalArgumentException; + + /** + * Compare the vector components of this vector with the given (x, y) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @return true if all the vector components are equal + */ + boolean equals(int x, int y); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3d.java new file mode 100644 index 000000000..9a00440d0 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3d.java @@ -0,0 +1,2613 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.*; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a Vector comprising 3 doubles and associated + * transformations. + * + * @author Richard Greenlees + * @author Kai Burjack + * @author F. Neurath + */ +public class Vector3d implements Externalizable, Cloneable, Vector3dc { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public double x; + /** + * The y component of the vector. + */ + public double y; + /** + * The z component of the vector. + */ + public double z; + + /** + * Create a new {@link Vector3d} with all components set to zero. + */ + public Vector3d() { + } + + /** + * Create a new {@link Vector3d} and initialize all three components with the given value. + * + * @param d + * the value of all three components + */ + public Vector3d(double d) { + this.x = d; + this.y = d; + this.z = d; + } + + /** + * Create a new {@link Vector3d} with the given component values. + * + * @param x + * the value of x + * @param y + * the value of y + * @param z + * the value of z + */ + public Vector3d(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Create a new {@link Vector3d} whose values will be copied from the given vector. + * + * @param v + * provides the initial values for the new vector + */ + public Vector3d(Vector3fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + } + + /** + * Create a new {@link Vector3d} whose values will be copied from the given vector. + * + * @param v + * provides the initial values for the new vector + */ + public Vector3d(Vector3ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + } + + /** + * Create a new {@link Vector3d} with the first two components from the + * given v and the given z + * + * @param v + * the {@link Vector2fc} to copy the values from + * @param z + * the z component + */ + public Vector3d(Vector2fc v, double z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + } + + /** + * Create a new {@link Vector3d} with the first two components from the + * given v and the given z + * + * @param v + * the {@link Vector2ic} to copy the values from + * @param z + * the z component + */ + public Vector3d(Vector2ic v, double z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + } + + /** + * Create a new {@link Vector3d} whose values will be copied from the given vector. + * + * @param v + * provides the initial values for the new vector + */ + public Vector3d(Vector3dc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + } + + /** + * Create a new {@link Vector3d} with the first two components from the + * given v and the given z + * + * @param v + * the {@link Vector2d} to copy the values from + * @param z + * the z component + */ + public Vector3d(Vector2dc v, double z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + } + + /** + * Create a new {@link Vector3d} and initialize its three components from the first + * three elements of the given array. + * + * @param xyz + * the array containing at least three elements + */ + public Vector3d(double[] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + } + + /** + * Create a new {@link Vector3d} and initialize its three components from the first + * three elements of the given array. + * + * @param xyz + * the array containing at least three elements + */ + public Vector3d(float[] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + } + + /** + * Create a new {@link Vector3d} and read this vector from the supplied {@link ByteBuffer} + * at the current buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #Vector3d(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer values will be read in x, y, z order + * @see #Vector3d(int, ByteBuffer) + */ + public Vector3d(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector3d} and read this vector from the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index the absolute position into the ByteBuffer + * @param buffer values will be read in x, y, z order + */ + public Vector3d(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector3d} and read this vector from the supplied {@link DoubleBuffer} + * at the current buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is read, use {@link #Vector3d(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer values will be read in x, y, z order + * @see #Vector3d(int, DoubleBuffer) + */ + public Vector3d(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector3d} and read this vector from the supplied {@link DoubleBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index the absolute position into the DoubleBuffer + * @param buffer values will be read in x, y, z order + */ + public Vector3d(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public double x() { + return this.x; + } + + public double y() { + return this.y; + } + + public double z() { + return this.z; + } + + /** + * Set the x, y and z components to match the supplied vector. + * + * @param v + * the vector to set this vector's components from + * @return this + */ + public Vector3d set(Vector3dc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + return this; + } + + /** + * Set the x, y and z components to match the supplied vector. + * + * @param v + * the vector to set this vector's components from + * @return this + */ + public Vector3d set(Vector3ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + return this; + } + + /** + * Set the first two components from the given v + * and the z component from the given z + * + * @param v + * the {@link Vector2dc} to copy the values from + * @param z + * the z component + * @return this + */ + public Vector3d set(Vector2dc v, double z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + return this; + } + + /** + * Set the first two components from the given v + * and the z component from the given z + * + * @param v + * the {@link Vector2ic} to copy the values from + * @param z + * the z component + * @return this + */ + public Vector3d set(Vector2ic v, double z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + return this; + } + + /** + * Set the x, y and z components to match the supplied vector. + * + * @param v + * the vector to set this vector's components from + * @return this + */ + public Vector3d set(Vector3fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + return this; + } + + /** + * Set the first two components from the given v + * and the z component from the given z + * + * @param v + * the {@link Vector2fc} to copy the values from + * @param z + * the z component + * @return this + */ + public Vector3d set(Vector2fc v, double z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + return this; + } + + /** + * Set the x, y, and z components to the supplied value. + * + * @param d + * the value of all three components + * @return this + */ + public Vector3d set(double d) { + this.x = d; + this.y = d; + this.z = d; + return this; + } + + /** + * Set the x, y and z components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @return this + */ + public Vector3d set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + /** + * Set the three components of this vector to the first three elements of the given array. + * + * @param xyz + * the array containing at least three elements + * @return this + */ + public Vector3d set(double[] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + return this; + } + + /** + * Set the three components of this vector to the first three elements of the given array. + * + * @param xyz + * the array containing at least three elements + * @return this + */ + public Vector3d set(float[] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #set(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z order + * @return this + * @see #set(int, ByteBuffer) + */ + public Vector3d set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z order + * @return this + */ + public Vector3d set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is read, use {@link #set(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z order + * @return this + * @see #set(int, DoubleBuffer) + */ + public Vector3d set(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * values will be read in x, y, z order + * @return this + */ + public Vector3d set(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 3 double values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector3d setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..2] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..2] + */ + public Vector3d setComponent(int component, double value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public DoubleBuffer get(DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public DoubleBuffer get(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer getf(ByteBuffer buffer) { + MemUtil.INSTANCE.putf(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer getf(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putf(this, index, buffer); + return buffer; + } + + public FloatBuffer get(FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector3dc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Subtract the supplied vector from this one. + * + * @param v + * the vector to subtract from this + * @return this + */ + public Vector3d sub(Vector3dc v) { + this.x = x - v.x(); + this.y = y - v.y(); + this.z = z - v.z(); + return this; + } + + public Vector3d sub(Vector3dc v, Vector3d dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + dest.z = z - v.z(); + return dest; + } + + /** + * Subtract the supplied vector from this one. + * + * @param v + * the vector to subtract from this + * @return this + */ + public Vector3d sub(Vector3fc v) { + this.x = x - v.x(); + this.y = y - v.y(); + this.z = z - v.z(); + return this; + } + + public Vector3d sub(Vector3fc v, Vector3d dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + dest.z = z - v.z(); + return dest; + } + + /** + * Subtract (x, y, z) from this vector. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @return this + */ + public Vector3d sub(double x, double y, double z) { + this.x = this.x - x; + this.y = this.y - y; + this.z = this.z - z; + return this; + } + + public Vector3d sub(double x, double y, double z, Vector3d dest) { + dest.x = this.x - x; + dest.y = this.y - y; + dest.z = this.z - z; + return dest; + } + + /** + * Add the supplied vector to this one. + * + * @param v + * the vector to add + * @return this + */ + public Vector3d add(Vector3dc v) { + this.x = x + v.x(); + this.y = y + v.y(); + this.z = z + v.z(); + return this; + } + + public Vector3d add(Vector3dc v, Vector3d dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + dest.z = z + v.z(); + return dest; + } + + /** + * Add the supplied vector to this one. + * + * @param v + * the vector to add + * @return this + */ + public Vector3d add(Vector3fc v) { + this.x = x + v.x(); + this.y = y + v.y(); + this.z = z + v.z(); + return this; + } + + public Vector3d add(Vector3fc v, Vector3d dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + dest.z = z + v.z(); + return dest; + } + + /** + * Increment the components of this vector by the given values. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @return this + */ + public Vector3d add(double x, double y, double z) { + this.x = this.x + x; + this.y = this.y + y; + this.z = this.z + z; + return this; + } + + public Vector3d add(double x, double y, double z, Vector3d dest) { + dest.x = this.x + x; + dest.y = this.y + y; + dest.z = this.z + z; + return dest; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector3d fma(Vector3dc a, Vector3dc b) { + this.x = Math.fma(a.x(), b.x(), x); + this.y = Math.fma(a.y(), b.y(), y); + this.z = Math.fma(a.z(), b.z(), z); + return this; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector3d fma(double a, Vector3dc b) { + this.x = Math.fma(a, b.x(), x); + this.y = Math.fma(a, b.y(), y); + this.z = Math.fma(a, b.z(), z); + return this; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector3d fma(Vector3fc a, Vector3fc b) { + this.x = Math.fma(a.x(), b.x(), x); + this.y = Math.fma(a.y(), b.y(), y); + this.z = Math.fma(a.z(), b.z(), z); + return this; + } + + public Vector3d fma(Vector3fc a, Vector3fc b, Vector3d dest) { + dest.x = Math.fma(a.x(), b.x(), x); + dest.y = Math.fma(a.y(), b.y(), y); + dest.z = Math.fma(a.z(), b.z(), z); + return dest; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector3d fma(double a, Vector3fc b) { + this.x = Math.fma(a, b.x(), x); + this.y = Math.fma(a, b.y(), y); + this.z = Math.fma(a, b.z(), z); + return this; + } + + public Vector3d fma(Vector3dc a, Vector3dc b, Vector3d dest) { + dest.x = Math.fma(a.x(), b.x(), x); + dest.y = Math.fma(a.y(), b.y(), y); + dest.z = Math.fma(a.z(), b.z(), z); + return dest; + } + + public Vector3d fma(double a, Vector3dc b, Vector3d dest) { + dest.x = Math.fma(a, b.x(), x); + dest.y = Math.fma(a, b.y(), y); + dest.z = Math.fma(a, b.z(), z); + return dest; + } + + public Vector3d fma(Vector3dc a, Vector3fc b, Vector3d dest) { + dest.x = Math.fma(a.x(), b.x(), x); + dest.y = Math.fma(a.y(), b.y(), y); + dest.z = Math.fma(a.z(), b.z(), z); + return dest; + } + + public Vector3d fma(double a, Vector3fc b, Vector3d dest) { + dest.x = Math.fma(a, b.x(), x); + dest.y = Math.fma(a, b.y(), y); + dest.z = Math.fma(a, b.z(), z); + return dest; + } + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in this. + * + * @param a + * the multiplicand + * @param b + * the addend + * @return this + */ + public Vector3d mulAdd(Vector3dc a, Vector3dc b) { + this.x = Math.fma(x, a.x(), b.x()); + this.y = Math.fma(y, a.y(), b.y()); + this.z = Math.fma(z, a.z(), b.z()); + return this; + } + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in this. + * + * @param a + * the multiplicand + * @param b + * the addend + * @return this + */ + public Vector3d mulAdd(double a, Vector3dc b) { + this.x = Math.fma(x, a, b.x()); + this.y = Math.fma(y, a, b.y()); + this.z = Math.fma(z, a, b.z()); + return this; + } + + public Vector3d mulAdd(Vector3dc a, Vector3dc b, Vector3d dest) { + dest.x = Math.fma(x, a.x(), b.x()); + dest.y = Math.fma(y, a.y(), b.y()); + dest.z = Math.fma(z, a.z(), b.z()); + return dest; + } + + public Vector3d mulAdd(double a, Vector3dc b, Vector3d dest) { + dest.x = Math.fma(x, a, b.x()); + dest.y = Math.fma(y, a, b.y()); + dest.z = Math.fma(z, a, b.z()); + return dest; + } + + public Vector3d mulAdd(Vector3fc a, Vector3dc b, Vector3d dest) { + dest.x = Math.fma(x, a.x(), b.x()); + dest.y = Math.fma(y, a.y(), b.y()); + dest.z = Math.fma(z, a.z(), b.z()); + return dest; + } + + /** + * Multiply this Vector3d component-wise by another Vector3dc. + * + * @param v + * the vector to multiply by + * @return this + */ + public Vector3d mul(Vector3dc v) { + this.x = x * v.x(); + this.y = y * v.y(); + this.z = z * v.z(); + return this; + } + + /** + * Multiply this Vector3d component-wise by another Vector3fc. + * + * @param v + * the vector to multiply by + * @return this + */ + public Vector3d mul(Vector3fc v) { + this.x = x * v.x(); + this.y = y * v.y(); + this.z = z * v.z(); + return this; + } + + public Vector3d mul(Vector3fc v, Vector3d dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + dest.z = z * v.z(); + return dest; + } + + public Vector3d mul(Vector3dc v, Vector3d dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + dest.z = z * v.z(); + return dest; + } + + /** + * Divide this Vector3d component-wise by another Vector3dc. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector3d div(Vector3d v) { + this.x = x / v.x(); + this.y = y / v.y(); + this.z = z / v.z(); + return this; + } + + /** + * Divide this Vector3d component-wise by another Vector3fc. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector3d div(Vector3fc v) { + this.x = x / v.x(); + this.y = y / v.y(); + this.z = z / v.z(); + return this; + } + + public Vector3d div(Vector3fc v, Vector3d dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + dest.z = z / v.z(); + return dest; + } + + public Vector3d div(Vector3dc v, Vector3d dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + dest.z = z / v.z(); + return dest; + } + + public Vector3d mulProject(Matrix4dc mat, double w, Vector3d dest) { + double invW = 1.0 / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))) * invW; + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))) * invW; + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))) * invW; + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3d mulProject(Matrix4dc mat, Vector3d dest) { + double invW = 1.0 / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))) * invW; + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))) * invW; + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))) * invW; + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the given matrix mat this Vector3d, perform perspective division. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulProject(Matrix4dc mat) { + double invW = 1.0 / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))) * invW; + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))) * invW; + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))) * invW; + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulProject(Matrix4fc mat, Vector3d dest) { + double invW = 1.0 / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + double rx = (mat.m00() * x + mat.m10() * y + mat.m20() * z + mat.m30()) * invW; + double ry = (mat.m01() * x + mat.m11() * y + mat.m21() * z + mat.m31()) * invW; + double rz = (mat.m02() * x + mat.m12() * y + mat.m22() * z + mat.m32()) * invW; + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the given matrix mat with this Vector3d, perform perspective division. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulProject(Matrix4fc mat) { + double invW = 1.0 / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + double rx = (mat.m00() * x + mat.m10() * y + mat.m20() * z + mat.m30()) * invW; + double ry = (mat.m01() * x + mat.m11() * y + mat.m21() * z + mat.m31()) * invW; + double rz = (mat.m02() * x + mat.m12() * y + mat.m22() * z + mat.m32()) * invW; + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Multiply the given matrix mat with this Vector3d. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mul(Matrix3fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Multiply the given matrix mat with this Vector3d. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mul(Matrix3dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mul(Matrix3dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3f mul(Matrix3dc mat, Vector3f dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + dest.x = (float) rx; + dest.y = (float) ry; + dest.z = (float) rz; + return dest; + } + + public Vector3d mul(Matrix3fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the given matrix with this Vector3d by assuming a third row in the matrix of (0, 0, 1) + * and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector3d mul(Matrix3x2dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + this.x = rx; + this.y = ry; + return this; + } + + public Vector3d mul(Matrix3x2dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + dest.x = rx; + dest.y = ry; + dest.z = z; + return dest; + } + + /** + * Multiply the given matrix with this Vector3d by assuming a third row in the matrix of (0, 0, 1) + * and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector3d mul(Matrix3x2fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + this.x = rx; + this.y = ry; + return this; + } + + public Vector3d mul(Matrix3x2fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + dest.x = rx; + dest.y = ry; + dest.z = z; + return dest; + } + + /** + * Multiply the transpose of the given matrix with this Vector3d and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector3d mulTranspose(Matrix3dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulTranspose(Matrix3dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the transpose of the given matrix with this Vector3d and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector3d mulTranspose(Matrix3fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulTranspose(Matrix3fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulPosition(Matrix4fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Multiply the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulPosition(Matrix4dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Multiply the given 4x3 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulPosition(Matrix4x3dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Multiply the given 4x3 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulPosition(Matrix4x3fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulPosition(Matrix4dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3d mulPosition(Matrix4fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3d mulPosition(Matrix4x3dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3d mulPosition(Matrix4x3fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the transpose of the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @return this + */ + public Vector3d mulTransposePosition(Matrix4dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, Math.fma(mat.m02(), z, mat.m03()))); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, Math.fma(mat.m12(), z, mat.m13()))); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, Math.fma(mat.m22(), z, mat.m23()))); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulTransposePosition(Matrix4dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, Math.fma(mat.m02(), z, mat.m03()))); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, Math.fma(mat.m12(), z, mat.m13()))); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, Math.fma(mat.m22(), z, mat.m23()))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the transpose of the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @return this + */ + public Vector3d mulTransposePosition(Matrix4fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, Math.fma(mat.m02(), z, mat.m03()))); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, Math.fma(mat.m12(), z, mat.m13()))); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, Math.fma(mat.m22(), z, mat.m23()))); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulTransposePosition(Matrix4fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, Math.fma(mat.m02(), z, mat.m03()))); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, Math.fma(mat.m12(), z, mat.m13()))); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, Math.fma(mat.m22(), z, mat.m23()))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the given 4x4 matrix mat with this and return the w component + * of the resulting 4D vector. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return the w component of the resulting 4D vector after multiplication + */ + public double mulPositionW(Matrix4fc mat) { + double w = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + this.x = rx; + this.y = ry; + this.z = rz; + return w; + } + + public double mulPositionW(Matrix4fc mat, Vector3d dest) { + double w = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return w; + } + + /** + * Multiply the given 4x4 matrix mat with this and return the w component + * of the resulting 4D vector. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return the w component of the resulting 4D vector after multiplication + */ + public double mulPositionW(Matrix4dc mat) { + double w = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + this.x = rx; + this.y = ry; + this.z = rz; + return w; + } + + public double mulPositionW(Matrix4dc mat, Vector3d dest) { + double w = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return w; + } + + /** + * Multiply the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulDirection(Matrix4fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Multiply the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulDirection(Matrix4dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Multiply the given 4x3 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulDirection(Matrix4x3dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Multiply the given 4x3 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3d mulDirection(Matrix4x3fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulDirection(Matrix4dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3d mulDirection(Matrix4fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3d mulDirection(Matrix4x3dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3d mulDirection(Matrix4x3fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the transpose of the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @return this + */ + public Vector3d mulTransposeDirection(Matrix4dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulTransposeDirection(Matrix4dc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply the transpose of the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @return this + */ + public Vector3d mulTransposeDirection(Matrix4fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d mulTransposeDirection(Matrix4fc mat, Vector3d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + double ry = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + double rz = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply this Vector3d by the given scalar value. + * + * @param scalar + * the scalar to multiply this vector by + * @return this + */ + public Vector3d mul(double scalar) { + this.x = x * scalar; + this.y = y * scalar; + this.z = z * scalar; + return this; + } + + public Vector3d mul(double scalar, Vector3d dest) { + dest.x = x * scalar; + dest.y = y * scalar; + dest.z = z * scalar; + return dest; + } + + /** + * Multiply the components of this Vector3d by the given scalar values and store the result in this. + * + * @param x + * the x component to multiply this vector by + * @param y + * the y component to multiply this vector by + * @param z + * the z component to multiply this vector by + * @return this + */ + public Vector3d mul(double x, double y, double z) { + this.x = this.x * x; + this.y = this.y * y; + this.z = this.z * z; + return this; + } + + public Vector3d mul(double x, double y, double z, Vector3d dest) { + dest.x = this.x * x; + dest.y = this.y * y; + dest.z = this.z * z; + return dest; + } + + /** + * Rotate this vector by the given quaternion quat and store the result in this. + * + * @see Quaterniond#transform(Vector3d) + * + * @param quat + * the quaternion to rotate this vector + * @return this + */ + public Vector3d rotate(Quaterniondc quat) { + return quat.transform(this, this); + } + + public Vector3d rotate(Quaterniondc quat, Vector3d dest) { + return quat.transform(this, dest); + } + + public Quaterniond rotationTo(Vector3dc toDir, Quaterniond dest) { + return dest.rotationTo(this, toDir); + } + + public Quaterniond rotationTo(double toDirX, double toDirY, double toDirZ, Quaterniond dest) { + return dest.rotationTo(x, y, z, toDirX, toDirY, toDirZ); + } + + /** + * Rotate this vector the specified radians around the given rotation axis. + * + * @param angle + * the angle in radians + * @param x + * the x component of the rotation axis + * @param y + * the y component of the rotation axis + * @param z + * the z component of the rotation axis + * @return this + */ + public Vector3d rotateAxis(double angle, double x, double y, double z) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotateX(x * angle, this); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotateY(y * angle, this); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotateZ(z * angle, this); + return rotateAxisInternal(angle, x, y, z, this); + } + + public Vector3d rotateAxis(double angle, double aX, double aY, double aZ, Vector3d dest) { + if (aY == 0.0 && aZ == 0.0 && Math.absEqualsOne(aX)) + return rotateX(aX * angle, dest); + else if (aX == 0.0 && aZ == 0.0 && Math.absEqualsOne(aY)) + return rotateY(aY * angle, dest); + else if (aX == 0.0 && aY == 0.0 && Math.absEqualsOne(aZ)) + return rotateZ(aZ * angle, dest); + return rotateAxisInternal(angle, aX, aY, aZ, dest); + } + + private Vector3d rotateAxisInternal(double angle, double aX, double aY, double aZ, Vector3d dest) { + double hangle = angle * 0.5; + double sinAngle = Math.sin(hangle); + double qx = aX * sinAngle, qy = aY * sinAngle, qz = aZ * sinAngle; + double qw = Math.cosFromSin(sinAngle, hangle); + double w2 = qw * qw, x2 = qx * qx, y2 = qy * qy, z2 = qz * qz, zw = qz * qw; + double xy = qx * qy, xz = qx * qz, yw = qy * qw, yz = qy * qz, xw = qx * qw; + double nx = (w2 + x2 - z2 - y2) * x + (-zw + xy - zw + xy) * y + (yw + xz + xz + yw) * z; + double ny = (xy + zw + zw + xy) * x + ( y2 - z2 + w2 - x2) * y + (yz + yz - xw - xw) * z; + double nz = (xz - yw + xz - yw) * x + ( yz + yz + xw + xw) * y + (z2 - y2 - x2 + w2) * z; + dest.x = nx; + dest.y = ny; + dest.z = nz; + return dest; + } + + /** + * Rotate this vector the specified radians around the X axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector3d rotateX(double angle) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double y = this.y * cos - this.z * sin; + double z = this.y * sin + this.z * cos; + this.y = y; + this.z = z; + return this; + } + + public Vector3d rotateX(double angle, Vector3d dest) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double y = this.y * cos - this.z * sin; + double z = this.y * sin + this.z * cos; + dest.x = this.x; + dest.y = y; + dest.z = z; + return dest; + } + + /** + * Rotate this vector the specified radians around the Y axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector3d rotateY(double angle) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double x = this.x * cos + this.z * sin; + double z = -this.x * sin + this.z * cos; + this.x = x; + this.z = z; + return this; + } + + public Vector3d rotateY(double angle, Vector3d dest) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double x = this.x * cos + this.z * sin; + double z = -this.x * sin + this.z * cos; + dest.x = x; + dest.y = this.y; + dest.z = z; + return dest; + } + + /** + * Rotate this vector the specified radians around the Z axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector3d rotateZ(double angle) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double x = this.x * cos - this.y * sin; + double y = this.x * sin + this.y * cos; + this.x = x; + this.y = y; + return this; + } + + public Vector3d rotateZ(double angle, Vector3d dest) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double x = this.x * cos - this.y * sin; + double y = this.x * sin + this.y * cos; + dest.x = x; + dest.y = y; + dest.z = this.z; + return dest; + } + + /** + * Divide this Vector3d by the given scalar value. + * + * @param scalar + * the scalar to divide this vector by + * @return this + */ + public Vector3d div(double scalar) { + double inv = 1.0 / scalar; + this.x = x * inv; + this.y = y * inv; + this.z = z * inv; + return this; + } + + public Vector3d div(double scalar, Vector3d dest) { + double inv = 1.0 / scalar; + dest.x = x * inv; + dest.y = y * inv; + dest.z = z * inv; + return dest; + } + + /** + * Divide the components of this Vector3d by the given scalar values and store the result in this. + * + * @param x + * the x component to divide this vector by + * @param y + * the y component to divide this vector by + * @param z + * the z component to divide this vector by + * @return this + */ + public Vector3d div(double x, double y, double z) { + this.x = this.x / x; + this.y = this.y / y; + this.z = this.z / z; + return this; + } + + public Vector3d div(double x, double y, double z, Vector3d dest) { + dest.x = this.x / x; + dest.y = this.y / y; + dest.z = this.z / z; + return dest; + } + + public double lengthSquared() { + return Math.fma(x, x, Math.fma(y, y, z * z)); + } + + /** + * Get the length squared of a 3-dimensional double-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * + * @return the length squared of the given vector + * + * @author F. Neurath + */ + public static double lengthSquared(double x, double y, double z) { + return Math.fma(x, x, Math.fma(y, y, z * z)); + } + + public double length() { + return Math.sqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + } + + /** + * Get the length of a 3-dimensional double-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * + * @return the length of the given vector + * + * @author F. Neurath + */ + public static double length(double x, double y, double z) { + return Math.sqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + } + + /** + * Normalize this vector. + * + * @return this + */ + public Vector3d normalize() { + double invLength = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + this.x = x * invLength; + this.y = y * invLength; + this.z = z * invLength; + return this; + } + + public Vector3d normalize(Vector3d dest) { + double invLength = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + dest.x = x * invLength; + dest.y = y * invLength; + dest.z = z * invLength; + return dest; + } + + /** + * Scale this vector to have the given length. + * + * @param length + * the desired length + * @return this + */ + public Vector3d normalize(double length) { + double invLength = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))) * length; + this.x = x * invLength; + this.y = y * invLength; + this.z = z * invLength; + return this; + } + + public Vector3d normalize(double length, Vector3d dest) { + double invLength = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))) * length; + dest.x = x * invLength; + dest.y = y * invLength; + dest.z = z * invLength; + return dest; + } + + /** + * Set this vector to be the cross product of this and v2. + * + * @param v + * the other vector + * @return this + */ + public Vector3d cross(Vector3dc v) { + double rx = Math.fma(y, v.z(), -z * v.y()); + double ry = Math.fma(z, v.x(), -x * v.z()); + double rz = Math.fma(x, v.y(), -y * v.x()); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Set this vector to be the cross product of itself and (x, y, z). + * + * @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 + * @return this + */ + public Vector3d cross(double x, double y, double z) { + double rx = Math.fma(this.y, z, -this.z * y); + double ry = Math.fma(this.z, x, -this.x * z); + double rz = Math.fma(this.x, y, -this.y * x); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3d cross(Vector3dc v, Vector3d dest) { + double rx = Math.fma(y, v.z(), -z * v.y()); + double ry = Math.fma(z, v.x(), -x * v.z()); + double rz = Math.fma(x, v.y(), -y * v.x()); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3d cross(double x, double y, double z, Vector3d dest) { + double rx = Math.fma(this.y, z, -this.z * y); + double ry = Math.fma(this.z, x, -this.x * z); + double rz = Math.fma(this.x, y, -this.y * x); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public double distance(Vector3dc v) { + double dx = this.x - v.x(); + double dy = this.y - v.y(); + double dz = this.z - v.z(); + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, dz * dz))); + } + + public double distance(double x, double y, double z) { + double dx = this.x - x; + double dy = this.y - y; + double dz = this.z - z; + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, dz * dz))); + } + + public double distanceSquared(Vector3dc v) { + double dx = this.x - v.x(); + double dy = this.y - v.y(); + double dz = this.z - v.z(); + return Math.fma(dx, dx, Math.fma(dy, dy, dz * dz)); + } + + public double distanceSquared(double x, double y, double z) { + double dx = this.x - x; + double dy = this.y - y; + double dz = this.z - z; + return Math.fma(dx, dx, Math.fma(dy, dy, dz * dz)); + } + + /** + * Return the distance between (x1, y1, z1) and (x2, y2, z2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @return the euclidean distance + */ + public static double distance(double x1, double y1, double z1, double x2, double y2, double z2) { + return Math.sqrt(distanceSquared(x1, y1, z1, x2, y2, z2)); + } + + /** + * Return the squared distance between (x1, y1, z1) and (x2, y2, z2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @return the euclidean distance squared + */ + public static double distanceSquared(double x1, double y1, double z1, double x2, double y2, double z2) { + double dx = x1 - x2; + double dy = y1 - y2; + double dz = z1 - z2; + return Math.fma(dx, dx, Math.fma(dy, dy, dz * dz)); + } + + public double dot(Vector3dc v) { + return Math.fma(this.x, v.x(), Math.fma(this.y, v.y(), this.z * v.z())); + } + + public double dot(double x, double y, double z) { + return Math.fma(this.x, x, Math.fma(this.y, y, this.z * z)); + } + + public double angleCos(Vector3dc v) { + double length1Squared = Math.fma(x, x, Math.fma(y, y, z * z)); + double length2Squared = Math.fma(v.x(), v.x(), Math.fma(v.y(), v.y(), v.z() * v.z())); + double dot = Math.fma(x, v.x(), Math.fma(y, v.y(), z * v.z())); + return dot / Math.sqrt(length1Squared * length2Squared); + } + + public double angle(Vector3dc v) { + double cos = angleCos(v); + // This is because sometimes cos goes above 1 or below -1 because of lost precision + cos = cos < 1 ? cos : 1; + cos = cos > -1 ? cos : -1; + return Math.acos(cos); + } + + public double angleSigned(Vector3dc v, Vector3dc n) { + double x = v.x(); + double y = v.y(); + double z = v.z(); + return Math.atan2( + (this.y * z - this.z * y) * n.x() + (this.z * x - this.x * z) * n.y() + (this.x * y - this.y * x) * n.z(), + this.x * x + this.y * y + this.z * z); + } + + public double angleSigned(double x, double y, double z, double nx, double ny, double nz) { + return Math.atan2( + (this.y * z - this.z * y) * nx + (this.z * x - this.x * z) * ny + (this.x * y - this.y * x) * nz, + this.x * x + this.y * y + this.z * z); + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector3d min(Vector3dc v) { + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + this.z = z < v.z() ? z : v.z(); + return this; + } + + public Vector3d min(Vector3dc v, Vector3d dest) { + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + dest.z = z < v.z() ? z : v.z(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector3d max(Vector3dc v) { + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + this.z = z > v.z() ? z : v.z(); + return this; + } + + public Vector3d max(Vector3dc v, Vector3d dest) { + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + dest.z = z > v.z() ? z : v.z(); + return dest; + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector3d zero() { + this.x = 0; + this.y = 0; + this.z = 0; + return this; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + " " + Runtime.format(z, formatter) + ")"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(x); + out.writeDouble(y); + out.writeDouble(z); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + x = in.readDouble(); + y = in.readDouble(); + z = in.readDouble(); + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector3d negate() { + this.x = -x; + this.y = -y; + this.z = -z; + return this; + } + + public Vector3d negate(Vector3d dest) { + dest.x = -x; + dest.y = -y; + dest.z = -z; + return dest; + } + + /** + * Set this vector's components to their respective absolute values. + * + * @return this + */ + public Vector3d absolute() { + this.x = Math.abs(this.x); + this.y = Math.abs(this.y); + this.z = Math.abs(this.z); + return this; + } + + public Vector3d absolute(Vector3d dest) { + dest.x = Math.abs(this.x); + dest.y = Math.abs(this.y); + dest.z = Math.abs(this.z); + return dest; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(x); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(y); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(z); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector3d other = (Vector3d) obj; + if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) + return false; + if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) + return false; + if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z)) + return false; + return true; + } + + public boolean equals(Vector3dc v, double delta) { + if (this == v) + return true; + if (v == null) + return false; + if (!(v instanceof Vector3dc)) + return false; + if (!Runtime.equals(x, v.x(), delta)) + return false; + if (!Runtime.equals(y, v.y(), delta)) + return false; + if (!Runtime.equals(z, v.z(), delta)) + return false; + return true; + } + + public boolean equals(double x, double y, double z) { + if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(x)) + return false; + if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(y)) + return false; + if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(z)) + return false; + return true; + } + + /** + * Reflect this vector about the given normal vector. + * + * @param normal + * the vector to reflect about + * @return this + */ + public Vector3d reflect(Vector3dc normal) { + double x = normal.x(); + double y = normal.y(); + double z = normal.z(); + double dot = Math.fma(this.x, x, Math.fma(this.y, y, this.z * z)); + this.x = this.x - (dot + dot) * x; + this.y = this.y - (dot + dot) * y; + this.z = this.z - (dot + dot) * z; + return this; + } + + /** + * Reflect this vector about the given normal vector. + * + * @param x + * the x component of the normal + * @param y + * the y component of the normal + * @param z + * the z component of the normal + * @return this + */ + public Vector3d reflect(double x, double y, double z) { + double dot = Math.fma(this.x, x, Math.fma(this.y, y, this.z * z)); + this.x = this.x - (dot + dot) * x; + this.y = this.y - (dot + dot) * y; + this.z = this.z - (dot + dot) * z; + return this; + } + + public Vector3d reflect(Vector3dc normal, Vector3d dest) { + double x = normal.x(); + double y = normal.y(); + double z = normal.z(); + double dot = Math.fma(this.x, x, Math.fma(this.y, y, this.z * z)); + dest.x = this.x - (dot + dot) * x; + dest.y = this.y - (dot + dot) * y; + dest.z = this.z - (dot + dot) * z; + return dest; + } + + public Vector3d reflect(double x, double y, double z, Vector3d dest) { + double dot = Math.fma(this.x, x, Math.fma(this.y, y, this.z * z)); + dest.x = this.x - (dot + dot) * x; + dest.y = this.y - (dot + dot) * y; + dest.z = this.z - (dot + dot) * z; + return dest; + } + + /** + * Compute the half vector between this and the other vector. + * + * @param other + * the other vector + * @return this + */ + public Vector3d half(Vector3dc other) { + return this.set(this).add(other.x(), other.y(), other.z()).normalize(); + } + + /** + * Compute the half vector between this and the vector (x, y, z). + * + * @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 + * @return this + */ + public Vector3d half(double x, double y, double z) { + return this.set(this).add(x, y, z).normalize(); + } + + public Vector3d half(Vector3dc other, Vector3d dest) { + return dest.set(this).add(other.x(), other.y(), other.z()).normalize(); + } + + public Vector3d half(double x, double y, double z, Vector3d dest) { + return dest.set(this).add(x, y, z).normalize(); + } + + public Vector3d smoothStep(Vector3dc v, double t, Vector3d dest) { + double t2 = t * t; + double t3 = t2 * t; + dest.x = (x + x - v.x() - v.x()) * t3 + (3.0 * v.x() - 3.0 * x) * t2 + x * t + x; + dest.y = (y + y - v.y() - v.y()) * t3 + (3.0 * v.y() - 3.0 * y) * t2 + y * t + y; + dest.z = (z + z - v.z() - v.z()) * t3 + (3.0 * v.z() - 3.0 * z) * t2 + z * t + z; + return dest; + } + + public Vector3d hermite(Vector3dc t0, Vector3dc v1, Vector3dc t1, double t, Vector3d dest) { + double t2 = t * t; + double t3 = t2 * t; + dest.x = (x + x - v1.x() - v1.x() + t1.x() + t0.x()) * t3 + (3.0 * v1.x() - 3.0 * x - t0.x() - t0.x() - t1.x()) * t2 + x * t + x; + dest.y = (y + y - v1.y() - v1.y() + t1.y() + t0.y()) * t3 + (3.0 * v1.y() - 3.0 * y - t0.y() - t0.y() - t1.y()) * t2 + y * t + y; + dest.z = (z + z - v1.z() - v1.z() + t1.z() + t0.z()) * t3 + (3.0 * v1.z() - 3.0 * z - t0.z() - t0.z() - t1.z()) * t2 + z * t + z; + return dest; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Vector3d lerp(Vector3dc other, double t) { + this.x = Math.fma(other.x() - x, t, x); + this.y = Math.fma(other.y() - y, t, y); + this.z = Math.fma(other.z() - z, t, z); + return this; + } + + public Vector3d lerp(Vector3dc other, double t, Vector3d dest) { + dest.x = Math.fma(other.x() - x, t, x); + dest.y = Math.fma(other.y() - y, t, y); + dest.z = Math.fma(other.z() - z, t, z); + return dest; + } + + public double get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw new IllegalArgumentException(); + } + } + + public Vector3i get(int mode, Vector3i dest) { + dest.x = Math.roundUsing(this.x(), mode); + dest.y = Math.roundUsing(this.y(), mode); + dest.z = Math.roundUsing(this.z(), mode); + return dest; + } + + public Vector3f get(Vector3f dest) { + dest.x = (float) this.x(); + dest.y = (float) this.y(); + dest.z = (float) this.z(); + return dest; + } + + public Vector3d get(Vector3d dest) { + dest.x = this.x(); + dest.y = this.y(); + dest.z = this.z(); + return dest; + } + + public int maxComponent() { + double absX = Math.abs(x); + double absY = Math.abs(y); + double absZ = Math.abs(z); + if (absX >= absY && absX >= absZ) { + return 0; + } else if (absY >= absZ) { + return 1; + } + return 2; + } + + public int minComponent() { + double absX = Math.abs(x); + double absY = Math.abs(y); + double absZ = Math.abs(z); + if (absX < absY && absX < absZ) { + return 0; + } else if (absY < absZ) { + return 1; + } + return 2; + } + + public Vector3d orthogonalize(Vector3dc v, Vector3d dest) { + /* + * http://lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts + */ + double rx, ry, rz; + if (Math.abs(v.x()) > Math.abs(v.z())) { + rx = -v.y(); + ry = v.x(); + rz = 0.0; + } else { + rx = 0.0; + ry = -v.z(); + rz = v.y(); + } + double invLen = Math.invsqrt(rx * rx + ry * ry + rz * rz); + dest.x = rx * invLen; + dest.y = ry * invLen; + dest.z = rz * invLen; + return dest; + } + + /** + * Transform this vector so that it is orthogonal to the given vector v and normalize the result. + *

+ * Reference: Gram–Schmidt process + * + * @param v + * the reference vector which the result should be orthogonal to + * @return this + */ + public Vector3d orthogonalize(Vector3dc v) { + return orthogonalize(v, this); + } + + public Vector3d orthogonalizeUnit(Vector3dc v, Vector3d dest) { + return orthogonalize(v, dest); + } + + /** + * Transform this vector so that it is orthogonal to the given unit vector v and normalize the result. + *

+ * The vector v is assumed to be a {@link #normalize() unit} vector. + *

+ * Reference: Gram–Schmidt process + * + * @param v + * the reference unit vector which the result should be orthogonal to + * @return this + */ + public Vector3d orthogonalizeUnit(Vector3dc v) { + return orthogonalizeUnit(v, this); + } + + /** + * Set each component of this vector to the largest (closest to positive + * infinity) {@code double} value that is less than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector3d floor() { + this.x = Math.floor(x); + this.y = Math.floor(y); + this.z = Math.floor(z); + return this; + } + + public Vector3d floor(Vector3d dest) { + dest.x = Math.floor(x); + dest.y = Math.floor(y); + dest.z = Math.floor(z); + return dest; + } + + /** + * Set each component of this vector to the smallest (closest to negative + * infinity) {@code double} value that is greater than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector3d ceil() { + this.x = Math.ceil(x); + this.y = Math.ceil(y); + this.z = Math.ceil(z); + return this; + } + + public Vector3d ceil(Vector3d dest) { + dest.x = Math.ceil(x); + dest.y = Math.ceil(y); + dest.z = Math.ceil(z); + return dest; + } + + /** + * Set each component of this vector to the closest double that is equal to + * a mathematical integer, with ties rounding to positive infinity. + * + * @return this + */ + public Vector3d round() { + this.x = Math.round(x); + this.y = Math.round(y); + this.z = Math.round(z); + return this; + } + + public Vector3d round(Vector3d dest) { + dest.x = Math.round(x); + dest.y = Math.round(y); + dest.z = Math.round(z); + return dest; + } + + public boolean isFinite() { + return Math.isFinite(x) && Math.isFinite(y) && Math.isFinite(z); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3dc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3dc.java new file mode 100644 index 000000000..148505f26 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3dc.java @@ -0,0 +1,1392 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.*; +import java.util.*; + +/** + * Interface to a read-only view of a 3-dimensional vector of double-precision floats. + * + * @author Kai Burjack + */ +public interface Vector3dc { + + /** + * @return the value of the x component + */ + double x(); + + /** + * @return the value of the y component + */ + double y(); + + /** + * @return the value of the z component + */ + double z(); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + * @see #get(int, ByteBuffer) + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + * @see #get(int, DoubleBuffer) + */ + DoubleBuffer get(DoubleBuffer buffer); + + /** + * Store this vector into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + DoubleBuffer get(int index, DoubleBuffer buffer); + + /** + * Store this vector into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + *

+ * Please note that due to this vector storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + * @see #get(int, DoubleBuffer) + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * Please note that due to this vector storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + *

+ * Please note that due to this vector storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + * + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + * @see #get(int, ByteBuffer) + */ + ByteBuffer getf(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this vector storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + ByteBuffer getf(int index, ByteBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector3dc getToAddress(long address); + + /** + * Subtract the supplied vector from this one and store the result in dest. + * + * @param v + * the vector to subtract from this + * @param dest + * will hold the result + * @return dest + */ + Vector3d sub(Vector3dc v, Vector3d dest); + + /** + * Subtract the supplied vector from this one and store the result in dest. + * + * @param v + * the vector to subtract from this + * @param dest + * will hold the result + * @return dest + */ + Vector3d sub(Vector3fc v, Vector3d dest); + + /** + * Subtract (x, y, z) from this vector and store the result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector3d sub(double x, double y, double z, Vector3d dest); + + /** + * Add the supplied vector to this one and store the result in dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector3d add(Vector3dc v, Vector3d dest); + + /** + * Add the supplied vector to this one and store the result in dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector3d add(Vector3fc v, Vector3d dest); + + /** + * Increment the components of this vector by the given values and store the result in dest. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @param dest + * will hold the result + * @return dest + */ + Vector3d add(double x, double y, double z, Vector3d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector3d fma(Vector3dc a, Vector3dc b, Vector3d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector3d fma(double a, Vector3dc b, Vector3d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector3d fma(Vector3dc a, Vector3fc b, Vector3d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector3d fma(Vector3fc a, Vector3fc b, Vector3d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector3d fma(double a, Vector3fc b, Vector3d dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulAdd(Vector3dc a, Vector3dc b, Vector3d dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulAdd(double a, Vector3dc b, Vector3d dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulAdd(Vector3fc a, Vector3dc b, Vector3d dest); + + /** + * Multiply this Vector3d component-wise by another Vector3f and store the result in dest. + * + * @param v + * the vector to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mul(Vector3fc v, Vector3d dest); + + /** + * Multiply this by v component-wise and store the result into dest. + * + * @param v + * the vector to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mul(Vector3dc v, Vector3d dest); + + /** + * Divide this Vector3d component-wise by another Vector3f and store the result in dest. + * + * @param v + * the vector to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector3d div(Vector3fc v, Vector3d dest); + + /** + * Divide this by v component-wise and store the result into dest. + * + * @param v + * the vector to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector3d div(Vector3dc v, Vector3d dest); + + /** + * Multiply the given matrix mat with this Vector3d, perform perspective division + * and store the result in dest. + *

+ * This method uses the given w as the fourth vector component. + * + * @param mat + * the matrix to multiply this vector by + * @param w + * the w component to use + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulProject(Matrix4dc mat, double w, Vector3d dest); + + /** + * Multiply the given matrix mat with this Vector3d, perform perspective division + * and store the result in dest. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulProject(Matrix4dc mat, Vector3d dest); + + /** + * Multiply the given matrix mat with this Vector3d, perform perspective division + * and store the result in dest. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulProject(Matrix4fc mat, Vector3d dest); + + /** + * Multiply the given matrix mat with this and store the + * result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mul(Matrix3dc mat, Vector3d dest); + + /** + * Multiply the given matrix mat with this and store the + * result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mul(Matrix3dc mat, Vector3f dest); + + /** + * Multiply the given matrix mat with this and store the + * result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mul(Matrix3fc mat, Vector3d dest); + + /** + * Multiply the given matrix mat with this by assuming a + * third row in the matrix of (0, 0, 1) and store the result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mul(Matrix3x2dc mat, Vector3d dest); + + /** + * Multiply the given matrix mat with this by assuming a + * third row in the matrix of (0, 0, 1) and store the result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mul(Matrix3x2fc mat, Vector3d dest); + + /** + * Multiply the transpose of the given matrix with this Vector3f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulTranspose(Matrix3dc mat, Vector3d dest); + + /** + * Multiply the transpose of the given matrix with this Vector3f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulTranspose(Matrix3fc mat, Vector3d dest); + + /** + * Multiply the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulPosition(Matrix4dc mat, Vector3d dest); + + /** + * Multiply the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulPosition(Matrix4fc mat, Vector3d dest); + + /** + * Multiply the given 4x3 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulPosition(Matrix4x3dc mat, Vector3d dest); + + /** + * Multiply the given 4x3 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulPosition(Matrix4x3fc mat, Vector3d dest); + + /** + * Multiply the transpose of the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulTransposePosition(Matrix4dc mat, Vector3d dest); + + /** + * Multiply the transpose of the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulTransposePosition(Matrix4fc mat, Vector3d dest); + + /** + * Multiply the given 4x4 matrix mat with this, store the + * result in dest and return the w component of the resulting 4D vector. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the (x, y, z) components of the resulting vector + * @return the w component of the resulting 4D vector after multiplication + */ + double mulPositionW(Matrix4fc mat, Vector3d dest); + + /** + * Multiply the given 4x4 matrix mat with this, store the + * result in dest and return the w component of the resulting 4D vector. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the (x, y, z) components of the resulting vector + * @return the w component of the resulting 4D vector after multiplication + */ + double mulPositionW(Matrix4dc mat, Vector3d dest); + + /** + * Multiply the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulDirection(Matrix4dc mat, Vector3d dest); + + /** + * Multiply the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulDirection(Matrix4fc mat, Vector3d dest); + + /** + * Multiply the given 4x3 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulDirection(Matrix4x3dc mat, Vector3d dest); + + /** + * Multiply the given 4x3 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulDirection(Matrix4x3fc mat, Vector3d dest); + + /** + * Multiply the transpose of the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulTransposeDirection(Matrix4dc mat, Vector3d dest); + + /** + * Multiply the transpose of the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulTransposeDirection(Matrix4fc mat, Vector3d dest); + + /** + * Multiply this Vector3d by the given scalar value and store the result in dest. + * + * @param scalar + * the scalar factor + * @param dest + * will hold the result + * @return dest + */ + Vector3d mul(double scalar, Vector3d dest); + + /** + * Multiply the components of this Vector3f by the given scalar values and store the result in dest. + * + * @param x + * the x component to multiply this vector by + * @param y + * the y component to multiply this vector by + * @param z + * the z component to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mul(double x, double y, double z, Vector3d dest); + + /** + * Rotate this vector by the given quaternion quat and store the result in dest. + * + * @see Quaterniond#transform(Vector3d) + * + * @param quat + * the quaternion to rotate this vector + * @param dest + * will hold the result + * @return dest + */ + Vector3d rotate(Quaterniondc quat, Vector3d dest); + + /** + * Compute the quaternion representing a rotation of this vector to point along toDir + * and store the result in dest. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + * + * @see Quaterniond#rotationTo(Vector3dc, Vector3dc) + * + * @param toDir + * the destination direction + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotationTo(Vector3dc toDir, Quaterniond dest); + + /** + * Compute the quaternion representing a rotation of this vector to point along (toDirX, toDirY, toDirZ) + * and store the result in dest. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + * + * @see Quaterniond#rotationTo(double, double, double, double, double, double) + * + * @param toDirX + * the x coordinate of the destination direction + * @param toDirY + * the y coordinate of the destination direction + * @param toDirZ + * the z coordinate of the destination direction + * @param dest + * will hold the result + * @return dest + */ + Quaterniond rotationTo(double toDirX, double toDirY, double toDirZ, Quaterniond dest); + + /** + * Rotate this vector the specified radians around the given rotation axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param aX + * the x component of the rotation axis + * @param aY + * the y component of the rotation axis + * @param aZ + * the z component of the rotation axis + * @param dest + * will hold the result + * @return dest + */ + Vector3d rotateAxis(double angle, double aX, double aY, double aZ, Vector3d dest); + + /** + * Rotate this vector the specified radians around the X axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector3d rotateX(double angle, Vector3d dest); + + /** + * Rotate this vector the specified radians around the Y axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector3d rotateY(double angle, Vector3d dest); + + /** + * Rotate this vector the specified radians around the Z axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector3d rotateZ(double angle, Vector3d dest); + + /** + * Divide this Vector3d by the given scalar value and store the result in dest. + * + * @param scalar + * the scalar to divide this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d div(double scalar, Vector3d dest); + + /** + * Divide the components of this Vector3f by the given scalar values and store the result in dest. + * + * @param x + * the x component to divide this vector by + * @param y + * the y component to divide this vector by + * @param z + * the z component to divide this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d div(double x, double y, double z, Vector3d dest); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + double lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + double length(); + + /** + * Normalize this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d normalize(Vector3d dest); + + /** + * Scale this vector to have the given length and store the result in dest. + * + * @param length + * the desired length + * @param dest + * will hold the result + * @return dest + */ + Vector3d normalize(double length, Vector3d dest); + + /** + * Calculate the cross product of this and v2 and store the result in dest. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3d cross(Vector3dc v, Vector3d dest); + + /** + * Compute the cross product of this vector and (x, y, z) and store the result in dest. + * + * @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 dest + * will hold the result + * @return dest + */ + Vector3d cross(double x, double y, double z, Vector3d dest); + + /** + * Return the distance between this vector and v. + * + * @param v + * the other vector + * @return the distance + */ + double distance(Vector3dc v); + + /** + * Return the distance between this vector and (x, y, z). + * + * @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 + * @return the euclidean distance + */ + double distance(double x, double y, double z); + + /** + * Return the square of the distance between this vector and v. + * + * @param v + * the other vector + * @return the squared of the distance + */ + double distanceSquared(Vector3dc v); + + /** + * Return the square of the distance between this vector and (x, y, z). + * + * @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 + * @return the square of the distance + */ + double distanceSquared(double x, double y, double z); + + /** + * Return the dot product of this vector and the supplied vector. + * + * @param v + * the other vector + * @return the dot product + */ + double dot(Vector3dc v); + + /** + * Return the dot product of this vector and the vector (x, y, z). + * + * @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 + * @return the dot product + */ + double dot(double x, double y, double z); + + /** + * Return the cosine of the angle between this vector and + * the supplied vector. Use this instead of Math.cos(angle(v)). + * + * @see #angle(Vector3dc) + * + * @param v + * the other vector + * @return the cosine of the angle + */ + double angleCos(Vector3dc v); + + /** + * Return the angle between this vector and the supplied vector. + * + * @see #angleCos(Vector3dc) + * + * @param v + * the other vector + * @return the angle, in radians + */ + double angle(Vector3dc v); + + /** + * Return the signed angle between this vector and the supplied vector with + * respect to the plane with the given normal vector n. + * + * @see #angleCos(Vector3dc) + * + * @param v + * the other vector + * @param n + * the plane's normal vector + * @return the angle, in radians + */ + double angleSigned(Vector3dc v, Vector3dc n); + + /** + * Return the signed angle between this vector and the supplied vector with + * respect to the plane with the given normal vector (nx, ny, nz). + * + * @param x + * the x coordinate of the other vector + * @param y + * the y coordinate of the other vector + * @param z + * the z coordinate of the other vector + * @param nx + * the x coordinate of the plane's normal vector + * @param ny + * the y coordinate of the plane's normal vector + * @param nz + * the z coordinate of the plane's normal vector + * @return the angle, in radians + */ + double angleSigned(double x, double y, double z, double nx, double ny, double nz); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3d min(Vector3dc v, Vector3d dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3d max(Vector3dc v, Vector3d dest); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d negate(Vector3d dest); + + /** + * Compute the absolute values of the individual components of this and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d absolute(Vector3d dest); + + /** + * Reflect this vector about the given normal vector and store the result in dest. + * + * @param normal + * the vector to reflect about + * @param dest + * will hold the result + * @return dest + */ + Vector3d reflect(Vector3dc normal, Vector3d dest); + + /** + * Reflect this vector about the given normal vector and store the result in dest. + * + * @param x + * the x component of the normal + * @param y + * the y component of the normal + * @param z + * the z component of the normal + * @param dest + * will hold the result + * @return dest + */ + Vector3d reflect(double x, double y, double z, Vector3d dest); + + /** + * Compute the half vector between this and the other vector and store the result in dest. + * + * @param other + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3d half(Vector3dc other, Vector3d dest); + + /** + * Compute the half vector between this and the vector (x, y, z) + * and store the result in dest. + * + * @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 dest + * will hold the result + * @return dest + */ + Vector3d half(double x, double y, double z, Vector3d dest); + + /** + * Compute a smooth-step (i.e. hermite with zero tangents) interpolation + * between this vector and the given vector v and + * store the result in dest. + * + * @param v + * the other vector + * @param t + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Vector3d smoothStep(Vector3dc v, double t, Vector3d dest); + + /** + * Compute a hermite interpolation between this vector and its + * associated tangent t0 and the given vector v + * with its tangent t1 and store the result in + * dest. + * + * @param t0 + * the tangent of this vector + * @param v1 + * the other vector + * @param t1 + * the tangent of the other vector + * @param t + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Vector3d hermite(Vector3dc t0, Vector3dc v1, Vector3dc t1, double t, Vector3d dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Vector3d lerp(Vector3dc other, double t, Vector3d dest); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..2] + * @return the value + * @throws IllegalArgumentException if component is not within [0..2] + */ + double get(int component) throws IllegalArgumentException; + + /** + * Set the components of the given vector dest to those of this vector + * using the given {@link RoundingMode}. + * + * @param mode + * the {@link RoundingMode} to use + * @param dest + * will hold the result + * @return dest + */ + Vector3i get(int mode, Vector3i dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f get(Vector3f dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d get(Vector3d dest); + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..2] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..2] + */ + int minComponent(); + + /** + * Transform this vector so that it is orthogonal to the given vector v, normalize the result and store it into dest. + *

+ * Reference: Gram–Schmidt process + * + * @param v + * the reference vector which the result should be orthogonal to + * @param dest + * will hold the result + * @return dest + */ + Vector3d orthogonalize(Vector3dc v, Vector3d dest); + + /** + * Transform this vector so that it is orthogonal to the given unit vector v, normalize the result and store it into dest. + *

+ * The vector v is assumed to be a {@link #normalize(Vector3d) unit} vector. + *

+ * Reference: Gram–Schmidt process + * + * @param v + * the reference unit vector which the result should be orthogonal to + * @param dest + * will hold the result + * @return dest + */ + Vector3d orthogonalizeUnit(Vector3dc v, Vector3d dest); + + /** + * Compute for each component of this vector the largest (closest to positive + * infinity) {@code double} value that is less than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d floor(Vector3d dest); + + /** + * Compute for each component of this vector the smallest (closest to negative + * infinity) {@code double} value that is greater than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d ceil(Vector3d dest); + + /** + * Compute for each component of this vector the closest double that is equal to + * a mathematical integer, with ties rounding to positive infinity and store + * the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d round(Vector3d dest); + + /** + * Determine whether all components are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + * Compare the vector components of this vector with the given vector using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param v + * the other vector + * @param delta + * the allowed maximum difference + * @return true whether all of the vector components are equal; false otherwise + */ + boolean equals(Vector3dc v, double delta); + + /** + * Compare the vector components of this vector with the given (x, y, z) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @param z + * the z component to compare to + * @return true if all the vector components are equal + */ + boolean equals(double x, double y, double z); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3f.java new file mode 100644 index 000000000..88c48a09e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3f.java @@ -0,0 +1,2086 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a Vector comprising 3 floats and associated + * transformations. + * + * @author Richard Greenlees + * @author Kai Burjack + * @author F. Neurath + */ +public class Vector3f implements Externalizable, Cloneable, Vector3fc { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public float x; + /** + * The y component of the vector. + */ + public float y; + /** + * The z component of the vector. + */ + public float z; + + /** + * Create a new {@link Vector3f} of (0, 0, 0). + */ + public Vector3f() { + } + + /** + * Create a new {@link Vector3f} and initialize all three components with the given value. + * + * @param d + * the value of all three components + */ + public Vector3f(float d) { + this.x = d; + this.y = d; + this.z = d; + } + + /** + * Create a new {@link Vector3f} with the given component values. + * + * @param x + * the value of x + * @param y + * the value of y + * @param z + * the value of z + */ + public Vector3f(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Create a new {@link Vector3f} with the same values as v. + * + * @param v + * the {@link Vector3fc} to copy the values from + */ + public Vector3f(Vector3fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + } + + /** + * Create a new {@link Vector3f} with the same values as v. + * + * @param v + * the {@link Vector3ic} to copy the values from + */ + public Vector3f(Vector3ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + } + + /** + * Create a new {@link Vector3f} with the first two components from the + * given v and the given z + * + * @param v + * the {@link Vector2fc} to copy the values from + * @param z + * the z component + */ + public Vector3f(Vector2fc v, float z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + } + + /** + * Create a new {@link Vector3f} with the first two components from the + * given v and the given z + * + * @param v + * the {@link Vector2ic} to copy the values from + * @param z + * the z component + */ + public Vector3f(Vector2ic v, float z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + } + + /** + * Create a new {@link Vector3f} and initialize its three components from the first + * three elements of the given array. + * + * @param xyz + * the array containing at least three elements + */ + public Vector3f(float[] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + } + + /** + * Create a new {@link Vector3f} and read this vector from the supplied {@link ByteBuffer} + * at the current buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #Vector3f(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer values will be read in x, y, z order + * @see #Vector3f(int, ByteBuffer) + */ + public Vector3f(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector3f} and read this vector from the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index the absolute position into the ByteBuffer + * @param buffer values will be read in x, y, z order + */ + public Vector3f(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector3f} and read this vector from the supplied {@link FloatBuffer} + * at the current buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is read, use {@link #Vector3f(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer values will be read in x, y, z order + * @see #Vector3f(int, FloatBuffer) + */ + public Vector3f(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector3f} and read this vector from the supplied {@link FloatBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index the absolute position into the FloatBuffer + * @param buffer values will be read in x, y, z order + */ + public Vector3f(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public float x() { + return this.x; + } + + public float y() { + return this.y; + } + + public float z() { + return this.z; + } + + /** + * Set the x, y and z components to match the supplied vector. + * + * @param v + * contains the values of x, y and z to set + * @return this + */ + public Vector3f set(Vector3fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + return this; + } + + /** + * Set the x, y and z components to match the supplied vector. + *

+ * Note that due to the given vector v storing the components in double-precision, + * there is the possibility to lose precision. + * + * @param v + * contains the values of x, y and z to set + * @return this + */ + public Vector3f set(Vector3dc v) { + this.x = (float) v.x(); + this.y = (float) v.y(); + this.z = (float) v.z(); + return this; + } + + /** + * Set the x, y and z components to match the supplied vector. + * + * @param v + * contains the values of x, y and z to set + * @return this + */ + public Vector3f set(Vector3ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + return this; + } + + /** + * Set the first two components from the given v + * and the z component from the given z + * + * @param v + * the {@link Vector2fc} to copy the values from + * @param z + * the z component + * @return this + */ + public Vector3f set(Vector2fc v, float z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + return this; + } + + /** + * Set the first two components from the given v + * and the z component from the given z + * + * @param v + * the {@link Vector2dc} to copy the values from + * @param z + * the z component + * @return this + */ + public Vector3f set(Vector2dc v, float z) { + this.x = (float) v.x(); + this.y = (float) v.y(); + this.z = z; + return this; + } + + /** + * Set the first two components from the given v + * and the z component from the given z + * + * @param v + * the {@link Vector2ic} to copy the values from + * @param z + * the z component + * @return this + */ + public Vector3f set(Vector2ic v, float z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + return this; + } + + /** + * Set the x, y, and z components to the supplied value. + * + * @param d + * the value of all three components + * @return this + */ + public Vector3f set(float d) { + this.x = d; + this.y = d; + this.z = d; + return this; + } + + /** + * Set the x, y and z components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @return this + */ + public Vector3f set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + /** + * Set the x, y, and z components to the supplied value. + * + * @param d + * the value of all three components + * @return this + */ + public Vector3f set(double d) { + this.x = (float) d; + this.y = (float) d; + this.z = (float) d; + return this; + } + + /** + * Set the x, y and z components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @return this + */ + public Vector3f set(double x, double y, double z) { + this.x = (float) x; + this.y = (float) y; + this.z = (float) z; + return this; + } + + /** + * Set the three components of this vector to the first three elements of the given array. + * + * @param xyz + * the array containing at least three elements + * @return this + */ + public Vector3f set(float[] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #set(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z order + * @return this + * @see #set(int, ByteBuffer) + */ + public Vector3f set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z order + * @return this + */ + public Vector3f set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is read, use {@link #set(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z order + * @return this + * @see #set(int, FloatBuffer) + */ + public Vector3f set(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * values will be read in x, y, z order + * @return this + */ + public Vector3f set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 3 float values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector3f setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..2] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..2] + */ + public Vector3f setComponent(int component, float value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public FloatBuffer get(FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector3fc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Subtract the supplied vector from this one and store the result in this. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector3f sub(Vector3fc v) { + this.x = x - v.x(); + this.y = y - v.y(); + this.z = z - v.z(); + return this; + } + + public Vector3f sub(Vector3fc v, Vector3f dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + dest.z = z - v.z(); + return dest; + } + + /** + * Decrement the components of this vector by the given values. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @return this + */ + public Vector3f sub(float x, float y, float z) { + this.x = this.x - x; + this.y = this.y - y; + this.z = this.z - z; + return this; + } + + public Vector3f sub(float x, float y, float z, Vector3f dest) { + dest.x = this.x - x; + dest.y = this.y - y; + dest.z = this.z - z; + return dest; + } + + /** + * Add the supplied vector to this one. + * + * @param v + * the vector to add + * @return this + */ + public Vector3f add(Vector3fc v) { + this.x = this.x + v.x(); + this.y = this.y + v.y(); + this.z = this.z + v.z(); + return this; + } + + public Vector3f add(Vector3fc v, Vector3f dest) { + dest.x = this.x + v.x(); + dest.y = this.y + v.y(); + dest.z = this.z + v.z(); + return dest; + } + + /** + * Increment the components of this vector by the given values. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @return this + */ + public Vector3f add(float x, float y, float z) { + this.x = this.x + x; + this.y = this.y + y; + this.z = this.z + z; + return this; + } + + public Vector3f add(float x, float y, float z, Vector3f dest) { + dest.x = this.x + x; + dest.y = this.y + y; + dest.z = this.z + z; + return dest; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector3f fma(Vector3fc a, Vector3fc b) { + this.x = Math.fma(a.x(), b.x(), x); + this.y = Math.fma(a.y(), b.y(), y); + this.z = Math.fma(a.z(), b.z(), z); + return this; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector3f fma(float a, Vector3fc b) { + this.x = Math.fma(a, b.x(), x); + this.y = Math.fma(a, b.y(), y); + this.z = Math.fma(a, b.z(), z); + return this; + } + + public Vector3f fma(Vector3fc a, Vector3fc b, Vector3f dest) { + dest.x = Math.fma(a.x(), b.x(), x); + dest.y = Math.fma(a.y(), b.y(), y); + dest.z = Math.fma(a.z(), b.z(), z); + return dest; + } + + public Vector3f fma(float a, Vector3fc b, Vector3f dest) { + dest.x = Math.fma(a, b.x(), x); + dest.y = Math.fma(a, b.y(), y); + dest.z = Math.fma(a, b.z(), z); + return dest; + } + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in this. + * + * @param a + * the multiplicand + * @param b + * the addend + * @return this + */ + public Vector3f mulAdd(Vector3fc a, Vector3fc b) { + this.x = Math.fma(x, a.x(), b.x()); + this.y = Math.fma(y, a.y(), b.y()); + this.z = Math.fma(z, a.z(), b.z()); + return this; + } + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in this. + * + * @param a + * the multiplicand + * @param b + * the addend + * @return this + */ + public Vector3f mulAdd(float a, Vector3fc b) { + this.x = Math.fma(x, a, b.x()); + this.y = Math.fma(y, a, b.y()); + this.z = Math.fma(z, a, b.z()); + return this; + } + + public Vector3f mulAdd(Vector3fc a, Vector3fc b, Vector3f dest) { + dest.x = Math.fma(x, a.x(), b.x()); + dest.y = Math.fma(y, a.y(), b.y()); + dest.z = Math.fma(z, a.z(), b.z()); + return dest; + } + + public Vector3f mulAdd(float a, Vector3fc b, Vector3f dest) { + dest.x = Math.fma(x, a, b.x()); + dest.y = Math.fma(y, a, b.y()); + dest.z = Math.fma(z, a, b.z()); + return dest; + } + + /** + * Multiply this Vector3f component-wise by another Vector3fc. + * + * @param v + * the vector to multiply by + * @return this + */ + public Vector3f mul(Vector3fc v) { + this.x = x * v.x(); + this.y = y * v.y(); + this.z = z * v.z(); + return this; + } + + public Vector3f mul(Vector3fc v, Vector3f dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + dest.z = z * v.z(); + return dest; + } + + /** + * Divide this Vector3f component-wise by another Vector3fc. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector3f div(Vector3fc v) { + this.x = this.x / v.x(); + this.y = this.y / v.y(); + this.z = this.z / v.z(); + return this; + } + + public Vector3f div(Vector3fc v, Vector3f dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + dest.z = z / v.z(); + return dest; + } + + public Vector3f mulProject(Matrix4fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + float invW = 1.0f / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))) * invW; + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))) * invW; + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))) * invW; + return dest; + } + + public Vector3f mulProject(Matrix4fc mat, float w, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + float invW = 1.0f / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))) * invW; + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))) * invW; + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))) * invW; + return dest; + } + + /** + * Multiply the given matrix mat with this Vector3f, perform perspective division. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3f mulProject(Matrix4fc mat) { + float x = this.x, y = this.y, z = this.z; + float invW = 1.0f / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))) * invW; + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))) * invW; + this.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))) * invW; + return this; + } + + /** + * Multiply the given matrix with this Vector3f and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector3f mul(Matrix3fc mat) { + float lx = x, ly = y, lz = z; + this.x = Math.fma(mat.m00(), lx, Math.fma(mat.m10(), ly, mat.m20() * lz)); + this.y = Math.fma(mat.m01(), lx, Math.fma(mat.m11(), ly, mat.m21() * lz)); + this.z = Math.fma(mat.m02(), lx, Math.fma(mat.m12(), ly, mat.m22() * lz)); + return this; + } + + public Vector3f mul(Matrix3fc mat, Vector3f dest) { + float lx = x, ly = y, lz = z; + dest.x = Math.fma(mat.m00(), lx, Math.fma(mat.m10(), ly, mat.m20() * lz)); + dest.y = Math.fma(mat.m01(), lx, Math.fma(mat.m11(), ly, mat.m21() * lz)); + dest.z = Math.fma(mat.m02(), lx, Math.fma(mat.m12(), ly, mat.m22() * lz)); + return dest; + } + + /** + * Multiply the given matrix with this Vector3f and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector3f mul(Matrix3dc mat) { + float lx = x, ly = y, lz = z; + this.x = (float) Math.fma(mat.m00(), lx, Math.fma(mat.m10(), ly, mat.m20() * lz)); + this.y = (float) Math.fma(mat.m01(), lx, Math.fma(mat.m11(), ly, mat.m21() * lz)); + this.z = (float) Math.fma(mat.m02(), lx, Math.fma(mat.m12(), ly, mat.m22() * lz)); + return this; + } + + public Vector3f mul(Matrix3dc mat, Vector3f dest) { + float lx = x, ly = y, lz = z; + dest.x = (float) Math.fma(mat.m00(), lx, Math.fma(mat.m10(), ly, mat.m20() * lz)); + dest.y = (float) Math.fma(mat.m01(), lx, Math.fma(mat.m11(), ly, mat.m21() * lz)); + dest.z = (float) Math.fma(mat.m02(), lx, Math.fma(mat.m12(), ly, mat.m22() * lz)); + return dest; + } + + /** + * Multiply the given matrix with this Vector3f and store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector3f mul(Matrix3x2fc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + this.z = z; + return this; + } + + public Vector3f mul(Matrix3x2fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + dest.z = z; + return dest; + } + + /** + * Multiply the transpose of the given matrix with this Vector3f store the result in this. + * + * @param mat + * the matrix + * @return this + */ + public Vector3f mulTranspose(Matrix3fc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + this.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + this.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + return this; + } + + public Vector3f mulTranspose(Matrix3fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + dest.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + dest.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + return dest; + } + + /** + * Multiply the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3f mulPosition(Matrix4fc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + this.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + return this; + } + + /** + * Multiply the given 4x3 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3f mulPosition(Matrix4x3fc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + this.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + return this; + } + + public Vector3f mulPosition(Matrix4fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + return dest; + } + + public Vector3f mulPosition(Matrix4x3fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + return dest; + } + + /** + * Multiply the transpose of the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @return this + */ + public Vector3f mulTransposePosition(Matrix4fc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, Math.fma(mat.m02(), z, mat.m03()))); + this.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, Math.fma(mat.m12(), z, mat.m13()))); + this.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, Math.fma(mat.m22(), z, mat.m23()))); + return this; + } + + public Vector3f mulTransposePosition(Matrix4fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, Math.fma(mat.m02(), z, mat.m03()))); + dest.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, Math.fma(mat.m12(), z, mat.m13()))); + dest.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, Math.fma(mat.m22(), z, mat.m23()))); + return dest; + } + + /** + * Multiply the given 4x4 matrix mat with this and return the w component + * of the resulting 4D vector. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @return the w component of the resulting 4D vector after multiplication + */ + public float mulPositionW(Matrix4fc mat) { + float x = this.x, y = this.y, z = this.z; + float w = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + this.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + return w; + } + + public float mulPositionW(Matrix4fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + float w = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33()))); + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30()))); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31()))); + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32()))); + return w; + } + + /** + * Multiply the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3f mulDirection(Matrix4dc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = (float) Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + this.y = (float) Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + this.z = (float) Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + return this; + } + + /** + * Multiply the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3f mulDirection(Matrix4fc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + this.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + return this; + } + + /** + * Multiply the given 4x3 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector3f mulDirection(Matrix4x3fc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + this.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + return this; + } + + public Vector3f mulDirection(Matrix4dc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = (float) Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + dest.y = (float) Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + dest.z = (float) Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + return dest; + } + + public Vector3f mulDirection(Matrix4fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + return dest; + } + + public Vector3f mulDirection(Matrix4x3fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, mat.m20() * z)); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, mat.m21() * z)); + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, mat.m22() * z)); + return dest; + } + + /** + * Multiply the transpose of the given 4x4 matrix mat with this. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @return this + */ + public Vector3f mulTransposeDirection(Matrix4fc mat) { + float x = this.x, y = this.y, z = this.z; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + this.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + this.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + return this; + } + + public Vector3f mulTransposeDirection(Matrix4fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + dest.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + dest.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + return dest; + } + + /** + * Multiply all components of this {@link Vector3f} by the given scalar + * value. + * + * @param scalar + * the scalar to multiply this vector by + * @return this + */ + public Vector3f mul(float scalar) { + this.x = this.x * scalar; + this.y = this.y * scalar; + this.z = this.z * scalar; + return this; + } + + public Vector3f mul(float scalar, Vector3f dest) { + dest.x = this.x * scalar; + dest.y = this.y * scalar; + dest.z = this.z * scalar; + return dest; + } + + /** + * Multiply the components of this Vector3f by the given scalar values and store the result in this. + * + * @param x + * the x component to multiply this vector by + * @param y + * the y component to multiply this vector by + * @param z + * the z component to multiply this vector by + * @return this + */ + public Vector3f mul(float x, float y, float z) { + this.x = this.x * x; + this.y = this.y * y; + this.z = this.z * z; + return this; + } + + public Vector3f mul(float x, float y, float z, Vector3f dest) { + dest.x = this.x * x; + dest.y = this.y * y; + dest.z = this.z * z; + return dest; + } + + /** + * Divide all components of this {@link Vector3f} by the given scalar + * value. + * + * @param scalar + * the scalar to divide by + * @return this + */ + public Vector3f div(float scalar) { + float inv = 1.0f / scalar; + this.x = this.x * inv; + this.y = this.y * inv; + this.z = this.z * inv; + return this; + } + + public Vector3f div(float scalar, Vector3f dest) { + float inv = 1.0f / scalar; + dest.x = this.x * inv; + dest.y = this.y * inv; + dest.z = this.z * inv; + return dest; + } + + /** + * Divide the components of this Vector3f by the given scalar values and store the result in this. + * + * @param x + * the x component to divide this vector by + * @param y + * the y component to divide this vector by + * @param z + * the z component to divide this vector by + * @return this + */ + public Vector3f div(float x, float y, float z) { + this.x = this.x / x; + this.y = this.y / y; + this.z = this.z / z; + return this; + } + + public Vector3f div(float x, float y, float z, Vector3f dest) { + dest.x = this.x / x; + dest.y = this.y / y; + dest.z = this.z / z; + return dest; + } + + /** + * Rotate this vector by the given quaternion quat and store the result in this. + * + * @see Quaternionfc#transform(Vector3f) + * + * @param quat + * the quaternion to rotate this vector + * @return this + */ + public Vector3f rotate(Quaternionfc quat) { + return quat.transform(this, this); + } + + public Vector3f rotate(Quaternionfc quat, Vector3f dest) { + return quat.transform(this, dest); + } + + public Quaternionf rotationTo(Vector3fc toDir, Quaternionf dest) { + return dest.rotationTo(this, toDir); + } + + public Quaternionf rotationTo(float toDirX, float toDirY, float toDirZ, Quaternionf dest) { + return dest.rotationTo(x, y, z, toDirX, toDirY, toDirZ); + } + + /** + * Rotate this vector the specified radians around the given rotation axis. + * + * @param angle + * the angle in radians + * @param x + * the x component of the rotation axis + * @param y + * the y component of the rotation axis + * @param z + * the z component of the rotation axis + * @return this + */ + public Vector3f rotateAxis(float angle, float x, float y, float z) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotateX(x * angle, this); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotateY(y * angle, this); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotateZ(z * angle, this); + return rotateAxisInternal(angle, x, y, z, this); + } + + public Vector3f rotateAxis(float angle, float aX, float aY, float aZ, Vector3f dest) { + if (aY == 0.0f && aZ == 0.0f && Math.absEqualsOne(aX)) + return rotateX(aX * angle, dest); + else if (aX == 0.0f && aZ == 0.0f && Math.absEqualsOne(aY)) + return rotateY(aY * angle, dest); + else if (aX == 0.0f && aY == 0.0f && Math.absEqualsOne(aZ)) + return rotateZ(aZ * angle, dest); + return rotateAxisInternal(angle, aX, aY, aZ, dest); + } + private Vector3f rotateAxisInternal(float angle, float aX, float aY, float aZ, Vector3f dest) { + float hangle = angle * 0.5f; + float sinAngle = Math.sin(hangle); + float qx = aX * sinAngle, qy = aY * sinAngle, qz = aZ * sinAngle; + float qw = Math.cosFromSin(sinAngle, hangle); + float w2 = qw * qw, x2 = qx * qx, y2 = qy * qy, z2 = qz * qz, zw = qz * qw; + float xy = qx * qy, xz = qx * qz, yw = qy * qw, yz = qy * qz, xw = qx * qw; + float x = this.x, y = this.y, z = this.z; + dest.x = (w2 + x2 - z2 - y2) * x + (-zw + xy - zw + xy) * y + (yw + xz + xz + yw) * z; + dest.y = (xy + zw + zw + xy) * x + ( y2 - z2 + w2 - x2) * y + (yz + yz - xw - xw) * z; + dest.z = (xz - yw + xz - yw) * x + ( yz + yz + xw + xw) * y + (z2 - y2 - x2 + w2) * z; + return dest; + } + + /** + * Rotate this vector the specified radians around the X axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector3f rotateX(float angle) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float y = this.y * cos - this.z * sin; + float z = this.y * sin + this.z * cos; + this.y = y; + this.z = z; + return this; + } + + public Vector3f rotateX(float angle, Vector3f dest) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float y = this.y * cos - this.z * sin; + float z = this.y * sin + this.z * cos; + dest.x = this.x; + dest.y = y; + dest.z = z; + return dest; + } + + /** + * Rotate this vector the specified radians around the Y axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector3f rotateY(float angle) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float x = this.x * cos + this.z * sin; + float z = -this.x * sin + this.z * cos; + this.x = x; + this.z = z; + return this; + } + + public Vector3f rotateY(float angle, Vector3f dest) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float x = this.x * cos + this.z * sin; + float z = -this.x * sin + this.z * cos; + dest.x = x; + dest.y = this.y; + dest.z = z; + return dest; + } + + /** + * Rotate this vector the specified radians around the Z axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector3f rotateZ(float angle) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float x = this.x * cos - this.y * sin; + float y = this.x * sin + this.y * cos; + this.x = x; + this.y = y; + return this; + } + + public Vector3f rotateZ(float angle, Vector3f dest) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float x = this.x * cos - this.y * sin; + float y = this.x * sin + this.y * cos; + dest.x = x; + dest.y = y; + dest.z = this.z; + return dest; + } + + public float lengthSquared() { + return Math.fma(x, x, Math.fma(y, y, z * z)); + } + + /** + * Get the length squared of a 3-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * + * @return the length squared of the given vector + * + * @author F. Neurath + */ + public static float lengthSquared(float x, float y, float z) { + return Math.fma(x, x, Math.fma(y, y, z * z)); + } + + public float length() { + return Math.sqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + } + + /** + * Get the length of a 3-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * + * @return the length of the given vector + * + * @author F. Neurath + */ + public static float length(float x, float y, float z) { + return Math.sqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + } + + /** + * Normalize this vector. + * + * @return this + */ + public Vector3f normalize() { + float scalar = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + this.x = this.x * scalar; + this.y = this.y * scalar; + this.z = this.z * scalar; + return this; + } + + public Vector3f normalize(Vector3f dest) { + float scalar = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + dest.x = this.x * scalar; + dest.y = this.y * scalar; + dest.z = this.z * scalar; + return dest; + } + + /** + * Scale this vector to have the given length. + * + * @param length + * the desired length + * @return this + */ + public Vector3f normalize(float length) { + float scalar = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))) * length; + this.x = this.x * scalar; + this.y = this.y * scalar; + this.z = this.z * scalar; + return this; + } + + public Vector3f normalize(float length, Vector3f dest) { + float scalar = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))) * length; + dest.x = this.x * scalar; + dest.y = this.y * scalar; + dest.z = this.z * scalar; + return dest; + } + + /** + * Set this vector to be the cross product of itself and v. + * + * @param v + * the other vector + * @return this + */ + public Vector3f cross(Vector3fc v) { + float rx = Math.fma(y, v.z(), -z * v.y()); + float ry = Math.fma(z, v.x(), -x * v.z()); + float rz = Math.fma(x, v.y(), -y * v.x()); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + /** + * Set this vector to be the cross product of itself and (x, y, z). + * + * @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 + * @return this + */ + public Vector3f cross(float x, float y, float z) { + float rx = Math.fma(this.y, z, -this.z * y); + float ry = Math.fma(this.z, x, -this.x * z); + float rz = Math.fma(this.x, y, -this.y * x); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector3f cross(Vector3fc v, Vector3f dest) { + float rx = Math.fma(y, v.z(), -z * v.y()); + float ry = Math.fma(z, v.x(), -x * v.z()); + float rz = Math.fma(x, v.y(), -y * v.x()); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public Vector3f cross(float x, float y, float z, Vector3f dest) { + float rx = Math.fma(this.y, z, -this.z * y); + float ry = Math.fma(this.z, x, -this.x * z); + float rz = Math.fma(this.x, y, -this.y * x); + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + public float distance(Vector3fc v) { + float dx = this.x - v.x(); + float dy = this.y - v.y(); + float dz = this.z - v.z(); + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, dz * dz))); + } + + public float distance(float x, float y, float z) { + float dx = this.x - x; + float dy = this.y - y; + float dz = this.z - z; + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, dz * dz))); + } + + public float distanceSquared(Vector3fc v) { + float dx = this.x - v.x(); + float dy = this.y - v.y(); + float dz = this.z - v.z(); + return Math.fma(dx, dx, Math.fma(dy, dy, dz * dz)); + } + + public float distanceSquared(float x, float y, float z) { + float dx = this.x - x; + float dy = this.y - y; + float dz = this.z - z; + return Math.fma(dx, dx, Math.fma(dy, dy, dz * dz)); + } + + /** + * Return the distance between (x1, y1, z1) and (x2, y2, z2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @return the euclidean distance + */ + public static float distance(float x1, float y1, float z1, float x2, float y2, float z2) { + return Math.sqrt(distanceSquared(x1, y1, z1, x2, y2, z2)); + } + + /** + * Return the squared distance between (x1, y1, z1) and (x2, y2, z2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @return the euclidean distance squared + */ + public static float distanceSquared(float x1, float y1, float z1, float x2, float y2, float z2) { + float dx = x1 - x2; + float dy = y1 - y2; + float dz = z1 - z2; + return Math.fma(dx, dx, Math.fma(dy, dy, dz * dz)); + } + + public float dot(Vector3fc v) { + return Math.fma(this.x, v.x(), Math.fma(this.y, v.y(), this.z * v.z())); + } + + public float dot(float x, float y, float z) { + return Math.fma(this.x, x, Math.fma(this.y, y, this.z * z)); + } + + public float angleCos(Vector3fc v) { + float x = this.x, y = this.y, z = this.z; + float length1Squared = Math.fma(x, x, Math.fma(y, y, z * z)); + float length2Squared = Math.fma(v.x(), v.x(), Math.fma(v.y(), v.y(), v.z() * v.z())); + float dot = Math.fma(x, v.x(), Math.fma(y, v.y(), z * v.z())); + return dot / (float)Math.sqrt(length1Squared * length2Squared); + } + + public float angle(Vector3fc v) { + float cos = angleCos(v); + // This is because sometimes cos goes above 1 or below -1 because of lost precision + cos = cos < 1 ? cos : 1; + cos = cos > -1 ? cos : -1; + return Math.acos(cos); + } + + public float angleSigned(Vector3fc v, Vector3fc n) { + return angleSigned(v.x(), v.y(), v.z(), n.x(), n.y(), n.z()); + } + + public float angleSigned(float x, float y, float z, float nx, float ny, float nz) { + float tx = this.x, ty = this.y, tz = this.z; + return Math.atan2( + (ty * z - tz * y) * nx + (tz * x - tx * z) * ny + (tx * y - ty * x) * nz, + tx * x + ty * y + tz * z); + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector3f min(Vector3fc v) { + float x = this.x, y = this.y, z = this.z; + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + this.z = z < v.z() ? z : v.z(); + return this; + } + + public Vector3f min(Vector3fc v, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + dest.z = z < v.z() ? z : v.z(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector3f max(Vector3fc v) { + float x = this.x, y = this.y, z = this.z; + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + this.z = z > v.z() ? z : v.z(); + return this; + } + + public Vector3f max(Vector3fc v, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + dest.z = z > v.z() ? z : v.z(); + return dest; + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector3f zero() { + this.x = 0; + this.y = 0; + this.z = 0; + return this; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + " " + Runtime.format(z, formatter) + ")"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(x); + out.writeFloat(y); + out.writeFloat(z); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + set(in.readFloat(), in.readFloat(), in.readFloat()); + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector3f negate() { + this.x = -x; + this.y = -y; + this.z = -z; + return this; + } + + public Vector3f negate(Vector3f dest) { + dest.x = -x; + dest.y = -y; + dest.z = -z; + return dest; + } + + /** + * Set this vector's components to their respective absolute values. + * + * @return this + */ + public Vector3f absolute() { + this.x = Math.abs(this.x); + this.y = Math.abs(this.y); + this.z = Math.abs(this.z); + return this; + } + + public Vector3f absolute(Vector3f dest) { + dest.x = Math.abs(this.x); + dest.y = Math.abs(this.y); + dest.z = Math.abs(this.z); + return dest; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(x); + result = prime * result + Float.floatToIntBits(y); + result = prime * result + Float.floatToIntBits(z); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector3f other = (Vector3f) obj; + if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) + return false; + if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) + return false; + if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z)) + return false; + return true; + } + + public boolean equals(Vector3fc v, float delta) { + if (this == v) + return true; + if (v == null) + return false; + if (!(v instanceof Vector3fc)) + return false; + if (!Runtime.equals(x, v.x(), delta)) + return false; + if (!Runtime.equals(y, v.y(), delta)) + return false; + if (!Runtime.equals(z, v.z(), delta)) + return false; + return true; + } + + public boolean equals(float x, float y, float z) { + if (Float.floatToIntBits(this.x) != Float.floatToIntBits(x)) + return false; + if (Float.floatToIntBits(this.y) != Float.floatToIntBits(y)) + return false; + if (Float.floatToIntBits(this.z) != Float.floatToIntBits(z)) + return false; + return true; + } + + /** + * Reflect this vector about the given normal vector. + * + * @param normal + * the vector to reflect about + * @return this + */ + public Vector3f reflect(Vector3fc normal) { + float x = normal.x(); + float y = normal.y(); + float z = normal.z(); + float dot = Math.fma(this.x, x, Math.fma(this.y, y, this.z * z)); + this.x = this.x - (dot + dot) * x; + this.y = this.y - (dot + dot) * y; + this.z = this.z - (dot + dot) * z; + return this; + } + + /** + * Reflect this vector about the given normal vector. + * + * @param x + * the x component of the normal + * @param y + * the y component of the normal + * @param z + * the z component of the normal + * @return this + */ + public Vector3f reflect(float x, float y, float z) { + float dot = Math.fma(this.x, x, Math.fma(this.y, y, this.z * z)); + this.x = this.x - (dot + dot) * x; + this.y = this.y - (dot + dot) * y; + this.z = this.z - (dot + dot) * z; + return this; + } + + public Vector3f reflect(Vector3fc normal, Vector3f dest) { + return reflect(normal.x(), normal.y(), normal.z(), dest); + } + + public Vector3f reflect(float x, float y, float z, Vector3f dest) { + float dot = this.dot(x, y, z); + dest.x = this.x - (dot + dot) * x; + dest.y = this.y - (dot + dot) * y; + dest.z = this.z - (dot + dot) * z; + return dest; + } + + /** + * Compute the half vector between this and the other vector. + * + * @param other + * the other vector + * @return this + */ + public Vector3f half(Vector3fc other) { + return this.set(this).add(other.x(), other.y(), other.z()).normalize(); + } + + /** + * Compute the half vector between this and the vector (x, y, z). + * + * @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 + * @return this + */ + public Vector3f half(float x, float y, float z) { + return half(x, y, z, this); + } + + public Vector3f half(Vector3fc other, Vector3f dest) { + return half(other.x(), other.y(), other.z(), dest); + } + + public Vector3f half(float x, float y, float z, Vector3f dest) { + return dest.set(this).add(x, y, z).normalize(); + } + + public Vector3f smoothStep(Vector3fc v, float t, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + float t2 = t * t; + float t3 = t2 * t; + dest.x = (x + x - v.x() - v.x()) * t3 + (3.0f * v.x() - 3.0f * x) * t2 + x * t + x; + dest.y = (y + y - v.y() - v.y()) * t3 + (3.0f * v.y() - 3.0f * y) * t2 + y * t + y; + dest.z = (z + z - v.z() - v.z()) * t3 + (3.0f * v.z() - 3.0f * z) * t2 + z * t + z; + return dest; + } + + public Vector3f hermite(Vector3fc t0, Vector3fc v1, Vector3fc t1, float t, Vector3f dest) { + float x = this.x, y = this.y, z = this.z; + float t2 = t * t; + float t3 = t2 * t; + dest.x = (x + x - v1.x() - v1.x() + t1.x() + t0.x()) * t3 + (3.0f * v1.x() - 3.0f * x - t0.x() - t0.x() - t1.x()) * t2 + x * t + x; + dest.y = (y + y - v1.y() - v1.y() + t1.y() + t0.y()) * t3 + (3.0f * v1.y() - 3.0f * y - t0.y() - t0.y() - t1.y()) * t2 + y * t + y; + dest.z = (z + z - v1.z() - v1.z() + t1.z() + t0.z()) * t3 + (3.0f * v1.z() - 3.0f * z - t0.z() - t0.z() - t1.z()) * t2 + z * t + z; + return dest; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Vector3f lerp(Vector3fc other, float t) { + return lerp(other, t, this); + } + + public Vector3f lerp(Vector3fc other, float t, Vector3f dest) { + dest.x = Math.fma(other.x() - x, t, x); + dest.y = Math.fma(other.y() - y, t, y); + dest.z = Math.fma(other.z() - z, t, z); + return dest; + } + + public float get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw new IllegalArgumentException(); + } + } + + public Vector3i get(int mode, Vector3i dest) { + dest.x = Math.roundUsing(this.x(), mode); + dest.y = Math.roundUsing(this.y(), mode); + dest.z = Math.roundUsing(this.z(), mode); + return dest; + } + + public Vector3f get(Vector3f dest) { + dest.x = this.x(); + dest.y = this.y(); + dest.z = this.z(); + return dest; + } + + public Vector3d get(Vector3d dest) { + dest.x = this.x(); + dest.y = this.y(); + dest.z = this.z(); + return dest; + } + + public int maxComponent() { + float absX = Math.abs(x); + float absY = Math.abs(y); + float absZ = Math.abs(z); + if (absX >= absY && absX >= absZ) { + return 0; + } else if (absY >= absZ) { + return 1; + } + return 2; + } + + public int minComponent() { + float absX = Math.abs(x); + float absY = Math.abs(y); + float absZ = Math.abs(z); + if (absX < absY && absX < absZ) { + return 0; + } else if (absY < absZ) { + return 1; + } + return 2; + } + + public Vector3f orthogonalize(Vector3fc v, Vector3f dest) { + /* + * http://lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts + */ + float rx, ry, rz; + if (Math.abs(v.x()) > Math.abs(v.z())) { + rx = -v.y(); + ry = v.x(); + rz = 0.0f; + } else { + rx = 0.0f; + ry = -v.z(); + rz = v.y(); + } + float invLen = Math.invsqrt(rx * rx + ry * ry + rz * rz); + dest.x = rx * invLen; + dest.y = ry * invLen; + dest.z = rz * invLen; + return dest; + } + + /** + * Transform this vector so that it is orthogonal to the given vector v and normalize the result. + *

+ * Reference: Gram–Schmidt process + * + * @param v + * the reference vector which the result should be orthogonal to + * @return this + */ + public Vector3f orthogonalize(Vector3fc v) { + return orthogonalize(v, this); + } + + public Vector3f orthogonalizeUnit(Vector3fc v, Vector3f dest) { + return orthogonalize(v, dest); + } + + /** + * Transform this vector so that it is orthogonal to the given unit vector v and normalize the result. + *

+ * The vector v is assumed to be a {@link #normalize() unit} vector. + *

+ * Reference: Gram–Schmidt process + * + * @param v + * the reference unit vector which the result should be orthogonal to + * @return this + */ + public Vector3f orthogonalizeUnit(Vector3fc v) { + return orthogonalizeUnit(v, this); + } + + /** + * Set each component of this vector to the largest (closest to positive + * infinity) {@code float} value that is less than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector3f floor() { + return floor(this); + } + + public Vector3f floor(Vector3f dest) { + dest.x = Math.floor(x); + dest.y = Math.floor(y); + dest.z = Math.floor(z); + return dest; + } + + /** + * Set each component of this vector to the smallest (closest to negative + * infinity) {@code float} value that is greater than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector3f ceil() { + return ceil(this); + } + + public Vector3f ceil(Vector3f dest) { + dest.x = Math.ceil(x); + dest.y = Math.ceil(y); + dest.z = Math.ceil(z); + return dest; + } + + /** + * Set each component of this vector to the closest float that is equal to + * a mathematical integer, with ties rounding to positive infinity. + * + * @return this + */ + public Vector3f round() { + return round(this); + } + + public Vector3f round(Vector3f dest) { + dest.x = Math.round(x); + dest.y = Math.round(y); + dest.z = Math.round(z); + return dest; + } + + public boolean isFinite() { + return Math.isFinite(x) && Math.isFinite(y) && Math.isFinite(z); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3fc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3fc.java new file mode 100644 index 000000000..2aa0dc0a0 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3fc.java @@ -0,0 +1,1089 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 3-dimensional vector of single-precision floats. + * + * @author Kai Burjack + */ +public interface Vector3fc { + + /** + * @return the value of the x component + */ + float x(); + + /** + * @return the value of the y component + */ + float y(); + + /** + * @return the value of the z component + */ + float z(); + + /** + * Store this vector into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, FloatBuffer) + * + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + * @see #get(int, FloatBuffer) + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + * @see #get(int, ByteBuffer) + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector3fc getToAddress(long address); + + /** + * Subtract the supplied vector from this one and store the result in dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector3f sub(Vector3fc v, Vector3f dest); + + /** + * Decrement the components of this vector by the given values and store the result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector3f sub(float x, float y, float z, Vector3f dest); + + /** + * Add the supplied vector to this one and store the result in dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector3f add(Vector3fc v, Vector3f dest); + + /** + * Increment the components of this vector by the given values and store the result in dest. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @param dest + * will hold the result + * @return dest + */ + Vector3f add(float x, float y, float z, Vector3f dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector3f fma(Vector3fc a, Vector3fc b, Vector3f dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector3f fma(float a, Vector3fc b, Vector3f dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulAdd(Vector3fc a, Vector3fc b, Vector3f dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulAdd(float a, Vector3fc b, Vector3f dest); + + /** + * Multiply this Vector3f component-wise by another Vector3f and store the result in dest. + * + * @param v + * the vector to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mul(Vector3fc v, Vector3f dest); + + /** + * Divide this Vector3f component-wise by another Vector3f and store the result in dest. + * + * @param v + * the vector to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector3f div(Vector3fc v, Vector3f dest); + + /** + * Multiply the given matrix mat with this Vector3f, perform perspective division + * and store the result in dest. + *

+ * This method uses w=1.0 as the fourth vector component. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulProject(Matrix4fc mat, Vector3f dest); + + /** + * Multiply the given matrix mat with this Vector3f, perform perspective division + * and store the result in dest. + *

+ * This method uses the given w as the fourth vector component. + * + * @param mat + * the matrix to multiply this vector by + * @param w + * the w component to use + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulProject(Matrix4fc mat, float w, Vector3f dest); + + /** + * Multiply the given matrix with this Vector3f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector3f mul(Matrix3fc mat, Vector3f dest); + + /** + * Multiply the given matrix with this Vector3f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector3f mul(Matrix3dc mat, Vector3f dest); + + /** + * Multiply the given matrix mat with this by assuming a + * third row in the matrix of (0, 0, 1) and store the result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mul(Matrix3x2fc mat, Vector3f dest); + + /** + * Multiply the transpose of the given matrix with this Vector3f and store the result in dest. + * + * @param mat + * the matrix + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulTranspose(Matrix3fc mat, Vector3f dest); + + /** + * Multiply the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulPosition(Matrix4fc mat, Vector3f dest); + + /** + * Multiply the given 4x3 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulPosition(Matrix4x3fc mat, Vector3f dest); + + /** + * Multiply the transpose of the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulTransposePosition(Matrix4fc mat, Vector3f dest); + + /** + * Multiply the given 4x4 matrix mat with this, store the + * result in dest and return the w component of the resulting 4D vector. + *

+ * This method assumes the w component of this to be 1.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the (x, y, z) components of the resulting vector + * @return the w component of the resulting 4D vector after multiplication + */ + float mulPositionW(Matrix4fc mat, Vector3f dest); + + /** + * Multiply the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulDirection(Matrix4dc mat, Vector3f dest); + + /** + * Multiply the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulDirection(Matrix4fc mat, Vector3f dest); + + /** + * Multiply the given 4x3 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulDirection(Matrix4x3fc mat, Vector3f dest); + + /** + * Multiply the transpose of the given 4x4 matrix mat with this and store the + * result in dest. + *

+ * This method assumes the w component of this to be 0.0. + * + * @param mat + * the matrix whose transpose to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulTransposeDirection(Matrix4fc mat, Vector3f dest); + + /** + * Multiply all components of this {@link Vector3f} by the given scalar + * value and store the result in dest. + * + * @param scalar + * the scalar to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mul(float scalar, Vector3f dest); + + /** + * Multiply the components of this Vector3f by the given scalar values and store the result in dest. + * + * @param x + * the x component to multiply this vector by + * @param y + * the y component to multiply this vector by + * @param z + * the z component to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mul(float x, float y, float z, Vector3f dest); + + /** + * Divide all components of this {@link Vector3f} by the given scalar + * value and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector3f div(float scalar, Vector3f dest); + + /** + * Divide the components of this Vector3f by the given scalar values and store the result in dest. + * + * @param x + * the x component to divide this vector by + * @param y + * the y component to divide this vector by + * @param z + * the z component to divide this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f div(float x, float y, float z, Vector3f dest); + + /** + * Rotate this vector by the given quaternion quat and store the result in dest. + * + * @see Quaternionfc#transform(Vector3f) + * + * @param quat + * the quaternion to rotate this vector + * @param dest + * will hold the result + * @return dest + */ + Vector3f rotate(Quaternionfc quat, Vector3f dest); + + /** + * Compute the quaternion representing a rotation of this vector to point along toDir + * and store the result in dest. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + * + * @see Quaternionf#rotationTo(Vector3fc, Vector3fc) + * + * @param toDir + * the destination direction + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotationTo(Vector3fc toDir, Quaternionf dest); + + /** + * Compute the quaternion representing a rotation of this vector to point along (toDirX, toDirY, toDirZ) + * and store the result in dest. + *

+ * Because there can be multiple possible rotations, this method chooses the one with the shortest arc. + * + * @see Quaternionf#rotationTo(float, float, float, float, float, float) + * + * @param toDirX + * the x coordinate of the destination direction + * @param toDirY + * the y coordinate of the destination direction + * @param toDirZ + * the z coordinate of the destination direction + * @param dest + * will hold the result + * @return dest + */ + Quaternionf rotationTo(float toDirX, float toDirY, float toDirZ, Quaternionf dest); + + /** + * Rotate this vector the specified radians around the given rotation axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param aX + * the x component of the rotation axis + * @param aY + * the y component of the rotation axis + * @param aZ + * the z component of the rotation axis + * @param dest + * will hold the result + * @return dest + */ + Vector3f rotateAxis(float angle, float aX, float aY, float aZ, Vector3f dest); + + /** + * Rotate this vector the specified radians around the X axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector3f rotateX(float angle, Vector3f dest); + + /** + * Rotate this vector the specified radians around the Y axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector3f rotateY(float angle, Vector3f dest); + + /** + * Rotate this vector the specified radians around the Z axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector3f rotateZ(float angle, Vector3f dest); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + float lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + float length(); + + /** + * Normalize this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f normalize(Vector3f dest); + + /** + * Scale this vector to have the given length and store the result in dest. + * + * @param length + * the desired length + * @param dest + * will hold the result + * @return dest + */ + Vector3f normalize(float length, Vector3f dest); + + /** + * Compute the cross product of this vector and v and store the result in dest. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3f cross(Vector3fc v, Vector3f dest); + + /** + * Compute the cross product of this vector and (x, y, z) and store the result in dest. + * + * @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 dest + * will hold the result + * @return dest + */ + Vector3f cross(float x, float y, float z, Vector3f dest); + + /** + * Return the distance between this Vector and v. + * + * @param v + * the other vector + * @return the distance + */ + float distance(Vector3fc v); + + /** + * Return the distance between this vector and (x, y, z). + * + * @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 + * @return the euclidean distance + */ + float distance(float x, float y, float z); + + /** + * Return the square of the distance between this vector and v. + * + * @param v + * the other vector + * @return the squared of the distance + */ + float distanceSquared(Vector3fc v); + + /** + * Return the square of the distance between this vector and (x, y, z). + * + * @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 + * @return the square of the distance + */ + float distanceSquared(float x, float y, float z); + + /** + * Return the dot product of this vector and the supplied vector. + * + * @param v + * the other vector + * @return the dot product + */ + float dot(Vector3fc v); + + /** + * Return the dot product of this vector and the vector (x, y, z). + * + * @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 + * @return the dot product + */ + float dot(float x, float y, float z); + + /** + * Return the cosine of the angle between this vector and the supplied vector. Use this instead of Math.cos(this.angle(v)). + * + * @see #angle(Vector3fc) + * + * @param v + * the other vector + * @return the cosine of the angle + */ + float angleCos(Vector3fc v); + + /** + * Return the angle between this vector and the supplied vector. + * + * @see #angleCos(Vector3fc) + * + * @param v + * the other vector + * @return the angle, in radians + */ + float angle(Vector3fc v); + + /** + * Return the signed angle between this vector and the supplied vector with + * respect to the plane with the given normal vector n. + * + * @see #angleCos(Vector3fc) + * + * @param v + * the other vector + * @param n + * the plane's normal vector + * @return the angle, in radians + */ + float angleSigned(Vector3fc v, Vector3fc n); + + /** + * Return the signed angle between this vector and the supplied vector with + * respect to the plane with the given normal vector (nx, ny, nz). + * + * @param x + * the x coordinate of the other vector + * @param y + * the y coordinate of the other vector + * @param z + * the z coordinate of the other vector + * @param nx + * the x coordinate of the plane's normal vector + * @param ny + * the y coordinate of the plane's normal vector + * @param nz + * the z coordinate of the plane's normal vector + * @return the angle, in radians + */ + float angleSigned(float x, float y, float z, float nx, float ny, float nz); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3f min(Vector3fc v, Vector3f dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3f max(Vector3fc v, Vector3f dest); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f negate(Vector3f dest); + + /** + * Compute the absolute values of the individual components of this and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f absolute(Vector3f dest); + + /** + * Reflect this vector about the given normal vector and store the result in dest. + * + * @param normal + * the vector to reflect about + * @param dest + * will hold the result + * @return dest + */ + Vector3f reflect(Vector3fc normal, Vector3f dest); + + /** + * Reflect this vector about the given normal vector and store the result in dest. + * + * @param x + * the x component of the normal + * @param y + * the y component of the normal + * @param z + * the z component of the normal + * @param dest + * will hold the result + * @return dest + */ + Vector3f reflect(float x, float y, float z, Vector3f dest); + + /** + * Compute the half vector between this and the other vector and store the result in dest. + * + * @param other + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3f half(Vector3fc other, Vector3f dest); + + /** + * Compute the half vector between this and the vector (x, y, z) + * and store the result in dest. + * + * @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 dest + * will hold the result + * @return dest + */ + Vector3f half(float x, float y, float z, Vector3f dest); + + /** + * Compute a smooth-step (i.e. hermite with zero tangents) interpolation + * between this vector and the given vector v and + * store the result in dest. + * + * @param v + * the other vector + * @param t + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Vector3f smoothStep(Vector3fc v, float t, Vector3f dest); + + /** + * Compute a hermite interpolation between this vector with its + * associated tangent t0 and the given vector v + * with its tangent t1 and store the result in + * dest. + * + * @param t0 + * the tangent of this vector + * @param v1 + * the other vector + * @param t1 + * the tangent of the other vector + * @param t + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Vector3f hermite(Vector3fc t0, Vector3fc v1, Vector3fc t1, float t, Vector3f dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Vector3f lerp(Vector3fc other, float t, Vector3f dest); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..2] + * @return the value + * @throws IllegalArgumentException if component is not within [0..2] + */ + float get(int component) throws IllegalArgumentException; + + /** + * Set the components of the given vector dest to those of this vector + * using the given {@link RoundingMode}. + * + * @param mode + * the {@link RoundingMode} to use + * @param dest + * will hold the result + * @return dest + */ + Vector3i get(int mode, Vector3i dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f get(Vector3f dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3d get(Vector3d dest); + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..2] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..2] + */ + int minComponent(); + + /** + * Transform this vector so that it is orthogonal to the given vector v, normalize the result and store it into dest. + *

+ * Reference: Gram–Schmidt process + * + * @param v + * the reference vector which the result should be orthogonal to + * @param dest + * will hold the result + * @return dest + */ + Vector3f orthogonalize(Vector3fc v, Vector3f dest); + + /** + * Transform this vector so that it is orthogonal to the given unit vector v, normalize the result and store it into dest. + *

+ * The vector v is assumed to be a {@link #normalize(Vector3f) unit} vector. + *

+ * Reference: Gram–Schmidt process + * + * @param v + * the reference unit vector which the result should be orthogonal to + * @param dest + * will hold the result + * @return dest + */ + Vector3f orthogonalizeUnit(Vector3fc v, Vector3f dest); + + /** + * Compute for each component of this vector the largest (closest to positive + * infinity) {@code float} value that is less than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f floor(Vector3f dest); + + /** + * Compute for each component of this vector the smallest (closest to negative + * infinity) {@code float} value that is greater than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f ceil(Vector3f dest); + + /** + * Compute for each component of this vector the closest float that is equal to + * a mathematical integer, with ties rounding to positive infinity and store + * the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3f round(Vector3f dest); + + /** + * Determine whether all components are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + * Compare the vector components of this vector with the given vector using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param v + * the other vector + * @param delta + * the allowed maximum difference + * @return true whether all of the vector components are equal; false otherwise + */ + boolean equals(Vector3fc v, float delta); + + /** + * Compare the vector components of this vector with the given (x, y, z) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @param z + * the z component to compare to + * @return true if all the vector components are equal + */ + boolean equals(float x, float y, float z); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3i.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3i.java new file mode 100644 index 000000000..ad920ae0f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3i.java @@ -0,0 +1,1131 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a Vector comprising 3 ints and associated + * transformations. + * + * @author Richard Greenlees + * @author Kai Burjack + * @author Hans Uhlig + */ +public class Vector3i implements Externalizable, Cloneable, Vector3ic { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public int x; + /** + * The y component of the vector. + */ + public int y; + /** + * The z component of the vector. + */ + public int z; + + /** + * Create a new {@link Vector3i} of (0, 0, 0). + */ + public Vector3i() { + } + + /** + * Create a new {@link Vector3i} and initialize all three components with + * the given value. + * + * @param d + * the value of all three components + */ + public Vector3i(int d) { + this.x = d; + this.y = d; + this.z = d; + } + + /** + * Create a new {@link Vector3i} with the given component values. + * + * @param x + * the value of x + * @param y + * the value of y + * @param z + * the value of z + */ + public Vector3i(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Create a new {@link Vector3i} with the same values as v. + * + * @param v + * the {@link Vector3ic} to copy the values from + */ + public Vector3i(Vector3ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + } + + /** + * Create a new {@link Vector3i} with the first two components from the + * given v and the given z + * + * @param v + * the {@link Vector2ic} to copy the values from + * @param z + * the z component + */ + public Vector3i(Vector2ic v, int z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + } + + /** + * Create a new {@link Vector3i} with the given component values and + * round using the given {@link RoundingMode}. + * + * @param x + * the value of x + * @param y + * the value of y + * @param z + * the value of z + * @param mode + * the {@link RoundingMode} to use + */ + public Vector3i(float x, float y, float z, int mode) { + this.x = Math.roundUsing(x, mode); + this.y = Math.roundUsing(y, mode); + this.z = Math.roundUsing(z, mode); + } + + /** + * Create a new {@link Vector3i} with the given component values and + * round using the given {@link RoundingMode}. + * + * @param x + * the value of x + * @param y + * the value of y + * @param z + * the value of z + * @param mode + * the {@link RoundingMode} to use + */ + public Vector3i(double x, double y, double z, int mode) { + this.x = Math.roundUsing(x, mode); + this.y = Math.roundUsing(y, mode); + this.z = Math.roundUsing(z, mode); + } + + /** + * Create a new {@link Vector3i} with the first two components from the + * given v and the given z and round using the given {@link RoundingMode}. + * + * @param v + * the {@link Vector2fc} to copy the values from + * @param z + * the z component + * @param mode + * the {@link RoundingMode} to use + */ + public Vector3i(Vector2fc v, float z, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + this.z = Math.roundUsing(z, mode); + } + + /** + * Create a new {@link Vector3i} and initialize its components to the rounded value of + * the given vector. + * + * @param v + * the {@link Vector3fc} to round and copy the values from + * @param mode + * the {@link RoundingMode} to use + */ + public Vector3i(Vector3fc v, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + this.z = Math.roundUsing(v.z(), mode); + } + + /** + * Create a new {@link Vector3i} with the first two components from the + * given v and the given z and round using the given {@link RoundingMode}. + * + * @param v + * the {@link Vector2dc} to copy the values from + * @param z + * the z component + * @param mode + * the {@link RoundingMode} to use + */ + public Vector3i(Vector2dc v, float z, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + this.z = Math.roundUsing(z, mode); + } + + /** + * Create a new {@link Vector3i} and initialize its components to the rounded value of + * the given vector. + * + * @param v + * the {@link Vector3dc} to round and copy the values from + * @param mode + * the {@link RoundingMode} to use + */ + public Vector3i(Vector3dc v, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + this.z = Math.roundUsing(v.z(), mode); + } + + /** + * Create a new {@link Vector3i} and initialize its three components from the first + * three elements of the given array. + * + * @param xyz + * the array containing at least three elements + */ + public Vector3i(int[] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + } + + /** + * Create a new {@link Vector3i} and read this vector from the supplied + * {@link ByteBuffer} at the current buffer + * {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * read, use {@link #Vector3i(int, ByteBuffer)}, taking the absolute + * position as parameter. + * + * @see #Vector3i(int, ByteBuffer) + * + * @param buffer + * values will be read in x, y, z order + */ + public Vector3i(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector3i} and read this vector from the supplied + * {@link ByteBuffer} starting at the specified absolute buffer + * position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z order + */ + public Vector3i(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector3i} and read this vector from the supplied + * {@link IntBuffer} at the current buffer + * {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * read, use {@link #Vector3i(int, IntBuffer)}, taking the absolute position + * as parameter. + * + * @see #Vector3i(int, IntBuffer) + * + * @param buffer + * values will be read in x, y, z order + */ + public Vector3i(IntBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector3i} and read this vector from the supplied + * {@link IntBuffer} starting at the specified absolute buffer + * position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param buffer + * values will be read in x, y, z order + */ + public Vector3i(int index, IntBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public int x() { + return this.x; + } + + public int y() { + return this.y; + } + + public int z() { + return this.z; + } + + /** + * Set the x, y and z components to match the supplied vector. + * + * @param v + * contains the values of x, y and z to set + * @return this + */ + public Vector3i set(Vector3ic v) { + x = v.x(); + y = v.y(); + z = v.z(); + return this; + } + + /** + * Set this {@link Vector3i} to the values of v using {@link RoundingMode#TRUNCATE} rounding. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector3i set(Vector3dc v) { + this.x = (int) v.x(); + this.y = (int) v.y(); + this.z = (int) v.z(); + return this; + } + + /** + * Set this {@link Vector3i} to the values of v using the given {@link RoundingMode}. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @param mode + * the {@link RoundingMode} to use + * @return this + */ + public Vector3i set(Vector3dc v, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + this.z = Math.roundUsing(v.z(), mode); + return this; + } + + /** + * Set this {@link Vector3i} to the values of v using the given {@link RoundingMode}. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @param mode + * the {@link RoundingMode} to use + * @return this + */ + public Vector3i set(Vector3fc v, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + this.z = Math.roundUsing(v.z(), mode); + return this; + } + + /** + * Set the first two components from the given v and the z + * component from the given z + * + * @param v + * the {@link Vector2ic} to copy the values from + * @param z + * the z component + * @return this + */ + public Vector3i set(Vector2ic v, int z) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + return this; + } + + /** + * Set the x, y, and z components to the supplied value. + * + * @param d + * the value of all three components + * @return this + */ + public Vector3i set(int d) { + this.x = d; + this.y = d; + this.z = d; + return this; + } + + /** + * Set the x, y and z components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @return this + */ + public Vector3i set(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + /** + * Set the three components of this vector to the first three elements of the given array. + * + * @param xyz + * the array containing at least three elements + * @return this + */ + public Vector3i set(int[] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * read, use {@link #set(int, ByteBuffer)}, taking the absolute position as + * parameter. + * + * @see #set(int, ByteBuffer) + * + * @param buffer + * values will be read in x, y, z order + * @return this + */ + public Vector3i set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z order + * @return this + */ + public Vector3i set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link IntBuffer} at the current + * buffer {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * read, use {@link #set(int, IntBuffer)}, taking the absolute position as + * parameter. + * + * @see #set(int, IntBuffer) + * + * @param buffer + * values will be read in x, y, z order + * @return this + */ + public Vector3i set(IntBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link IntBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param buffer + * values will be read in x, y, z order + * @return this + */ + public Vector3i set(int index, IntBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 3 integer values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector3i setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + public int get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw new IllegalArgumentException(); + } + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..2] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..2] + */ + public Vector3i setComponent(int component, int value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public IntBuffer get(IntBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public IntBuffer get(int index, IntBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector3ic getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Subtract the supplied vector from this one and store the result in + * this. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector3i sub(Vector3ic v) { + this.x = this.x - v.x(); + this.y = this.y - v.y(); + this.z = this.z - v.z(); + return this; + } + + public Vector3i sub(Vector3ic v, Vector3i dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + dest.z = z - v.z(); + return dest; + } + + /** + * Decrement the components of this vector by the given values. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @return this + */ + public Vector3i sub(int x, int y, int z) { + this.x = this.x - x; + this.y = this.y - y; + this.z = this.z - z; + return this; + } + + public Vector3i sub(int x, int y, int z, Vector3i dest) { + dest.x = this.x - x; + dest.y = this.y - y; + dest.z = this.z - z; + return dest; + } + + /** + * Add the supplied vector to this one. + * + * @param v + * the vector to add + * @return this + */ + public Vector3i add(Vector3ic v) { + this.x = this.x + v.x(); + this.y = this.y + v.y(); + this.z = this.z + v.z(); + return this; + } + + public Vector3i add(Vector3ic v, Vector3i dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + dest.z = z + v.z(); + return dest; + } + + /** + * Increment the components of this vector by the given values. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @return this + */ + public Vector3i add(int x, int y, int z) { + this.x = this.x + x; + this.y = this.y + y; + this.z = this.z + z; + return this; + } + + public Vector3i add(int x, int y, int z, Vector3i dest) { + dest.x = this.x + x; + dest.y = this.y + y; + dest.z = this.z + z; + return dest; + } + + /** + * Multiply all components of this {@link Vector3i} by the given scalar + * value. + * + * @param scalar + * the scalar to multiply this vector by + * @return this + */ + public Vector3i mul(int scalar) { + this.x = x * scalar; + this.y = y * scalar; + this.z = z * scalar; + return this; + } + + public Vector3i mul(int scalar, Vector3i dest) { + dest.x = x * scalar; + dest.y = y * scalar; + dest.z = z * scalar; + return dest; + } + + /** + * Multiply all components of this {@link Vector3i} by the given vector. + * + * @param v + * the vector to multiply + * @return this + */ + public Vector3i mul(Vector3ic v) { + this.x = this.x * v.x(); + this.y = this.y * v.y(); + this.z = this.z * v.z(); + return this; + } + + public Vector3i mul(Vector3ic v, Vector3i dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + dest.z = z * v.z(); + return dest; + } + + /** + * Multiply the components of this vector by the given values. + * + * @param x + * the x component to multiply + * @param y + * the y component to multiply + * @param z + * the z component to multiply + * @return this + */ + public Vector3i mul(int x, int y, int z) { + this.x = this.x * x; + this.y = this.y * y; + this.z = this.z * z; + return this; + } + + public Vector3i mul(int x, int y, int z, Vector3i dest) { + dest.x = this.x * x; + dest.y = this.y * y; + dest.z = this.z * z; + return dest; + } + + /** + * Divide all components of this {@link Vector3i} by the given scalar value. + * + * @param scalar + * the scalar to divide by + * @return this + */ + public Vector3i div(float scalar) { + float invscalar = 1.0f / scalar; + this.x = (int) (x * invscalar); + this.y = (int) (y * invscalar); + this.z = (int) (z * invscalar); + return this; + } + + public Vector3i div(float scalar, Vector3i dest) { + float invscalar = 1.0f / scalar; + dest.x = (int) (x * invscalar); + dest.y = (int) (y * invscalar); + dest.z = (int) (z * invscalar); + return dest; + } + + /** + * Divide all components of this {@link Vector3i} by the given scalar value. + * + * @param scalar + * the scalar to divide by + * @return this + */ + public Vector3i div(int scalar) { + this.x = x / scalar; + this.y = y / scalar; + this.z = z / scalar; + return this; + } + + public Vector3i div(int scalar, Vector3i dest) { + dest.x = x / scalar; + dest.y = y / scalar; + dest.z = z / scalar; + return dest; + } + + public long lengthSquared() { + return x * x + y * y + z * z; + } + + /** + * Get the length squared of a 3-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * + * @return the length squared of the given vector + */ + public static long lengthSquared(int x, int y, int z) { + return x * x + y * y + z * z; + } + + public double length() { + return Math.sqrt(x * x + y * y + z * z); + } + + /** + * Get the length of a 3-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * + * @return the length squared of the given vector + */ + public static double length(int x, int y, int z) { + return Math.sqrt(x * x + y * y + z * z); + } + + public double distance(Vector3ic v) { + int dx = this.x - v.x(); + int dy = this.y - v.y(); + int dz = this.z - v.z(); + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } + + public double distance(int x, int y, int z) { + int dx = this.x - x; + int dy = this.y - y; + int dz = this.z - z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } + + public long gridDistance(Vector3ic v) { + return Math.abs(v.x() - x()) + Math.abs(v.y() - y()) + Math.abs(v.z() - z()); + } + + public long gridDistance(int x, int y, int z) { + return Math.abs(x - x()) + Math.abs(y - y()) + Math.abs(z - z()); + } + + public long distanceSquared(Vector3ic v) { + int dx = this.x - v.x(); + int dy = this.y - v.y(); + int dz = this.z - v.z(); + return dx * dx + dy * dy + dz * dz; + } + + public long distanceSquared(int x, int y, int z) { + int dx = this.x - x; + int dy = this.y - y; + int dz = this.z - z; + return dx * dx + dy * dy + dz * dz; + } + + /** + * Return the distance between (x1, y1, z1) and (x2, y2, z2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @return the euclidean distance + */ + public static double distance(int x1, int y1, int z1, int x2, int y2, int z2) { + return Math.sqrt(distanceSquared(x1, y1, z1, x2, y2, z2)); + } + + /** + * Return the squared distance between (x1, y1, z1) and (x2, y2, z2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @return the euclidean distance squared + */ + public static long distanceSquared(int x1, int y1, int z1, int x2, int y2, int z2) { + int dx = x1 - x2; + int dy = y1 - y2; + int dz = z1 - z2; + return dx * dx + dy * dy + dz * dz; + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector3i zero() { + this.x = 0; + this.y = 0; + this.z = 0; + return this; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + formatter.format(x) + " " + formatter.format(y) + " " + formatter.format(z) + ")"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(x); + out.writeInt(y); + out.writeInt(z); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + x = in.readInt(); + y = in.readInt(); + z = in.readInt(); + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector3i negate() { + this.x = -x; + this.y = -y; + this.z = -z; + return this; + } + + public Vector3i negate(Vector3i dest) { + dest.x = -x; + dest.y = -y; + dest.z = -z; + return dest; + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector3i min(Vector3ic v) { + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + this.z = z < v.z() ? z : v.z(); + return this; + } + + public Vector3i min(Vector3ic v, Vector3i dest) { + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + dest.z = z < v.z() ? z : v.z(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector3i max(Vector3ic v) { + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + this.z = z > v.z() ? z : v.z(); + return this; + } + + public Vector3i max(Vector3ic v, Vector3i dest) { + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + dest.z = z > v.z() ? z : v.z(); + return dest; + } + + public int maxComponent() { + float absX = Math.abs(x); + float absY = Math.abs(y); + float absZ = Math.abs(z); + if (absX >= absY && absX >= absZ) { + return 0; + } else if (absY >= absZ) { + return 1; + } + return 2; + } + + public int minComponent() { + float absX = Math.abs(x); + float absY = Math.abs(y); + float absZ = Math.abs(z); + if (absX < absY && absX < absZ) { + return 0; + } else if (absY < absZ) { + return 1; + } + return 2; + } + + /** + * Set this vector's components to their respective absolute values. + * + * @return this + */ + public Vector3i absolute() { + this.x = Math.abs(this.x); + this.y = Math.abs(this.y); + this.z = Math.abs(this.z); + return this; + } + + public Vector3i absolute(Vector3i dest) { + dest.x = Math.abs(this.x); + dest.y = Math.abs(this.y); + dest.z = Math.abs(this.z); + return dest; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + x; + result = prime * result + y; + result = prime * result + z; + return result; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Vector3i other = (Vector3i) obj; + if (x != other.x) { + return false; + } + if (y != other.y) { + return false; + } + if (z != other.z) { + return false; + } + return true; + } + + public boolean equals(int x, int y, int z) { + if (this.x != x) + return false; + if (this.y != y) + return false; + if (this.z != z) + return false; + return true; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3ic.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3ic.java new file mode 100644 index 000000000..97407e19b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector3ic.java @@ -0,0 +1,409 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +/** + * Interface to a read-only view of a 3-dimensional vector of integers. + * + * @author Kai Burjack + */ +public interface Vector3ic { + + /** + * @return the value of the x component + */ + int x(); + + /** + * @return the value of the y component + */ + int y(); + + /** + * @return the value of the z component + */ + int z(); + + /** + * Store this vector into the supplied {@link IntBuffer} at the current + * buffer {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * stored, use {@link #get(int, IntBuffer)}, taking the absolute position as + * parameter. + * + * @see #get(int, IntBuffer) + * + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + IntBuffer get(IntBuffer buffer); + + /** + * Store this vector into the supplied {@link IntBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param + * buffer will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + IntBuffer get(int index, IntBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * stored, use {@link #get(int, ByteBuffer)}, taking the absolute position + * as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y, z order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector3ic getToAddress(long address); + + /** + * Subtract the supplied vector from this one and store the result in + * dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector3i sub(Vector3ic v, Vector3i dest); + + /** + * Decrement the components of this vector by the given values and store the + * result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector3i sub(int x, int y, int z, Vector3i dest); + + /** + * Add the supplied vector to this one and store the result in + * dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector3i add(Vector3ic v, Vector3i dest); + + /** + * Increment the components of this vector by the given values and store the + * result in dest. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @param dest + * will hold the result + * @return dest + */ + Vector3i add(int x, int y, int z, Vector3i dest); + + /** + * Multiply the components of this vector by the given scalar and store the result in dest. + * + * @param scalar + * the value to multiply this vector's components by + * @param dest + * will hold the result + * @return dest + */ + Vector3i mul(int scalar, Vector3i dest); + + /** + * Multiply the supplied vector by this one and store the result in + * dest. + * + * @param v + * the vector to multiply + * @param dest + * will hold the result + * @return dest + */ + Vector3i mul(Vector3ic v, Vector3i dest); + + /** + * Multiply the components of this vector by the given values and store the + * result in dest. + * + * @param x + * the x component to multiply + * @param y + * the y component to multiply + * @param z + * the z component to multiply + * @param dest + * will hold the result + * @return dest + */ + Vector3i mul(int x, int y, int z, Vector3i dest); + + /** + * Divide all components of this {@link Vector3i} by the given scalar value + * and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector3i div(float scalar, Vector3i dest); + + /** + * Divide all components of this {@link Vector3i} by the given scalar value + * and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector3i div(int scalar, Vector3i dest); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + long lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + double length(); + + /** + * Return the distance between this Vector and v. + * + * @param v + * the other vector + * @return the distance + */ + double distance(Vector3ic v); + + /** + * Return the distance between this vector and (x, y, z). + * + * @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 + * @return the euclidean distance + */ + double distance(int x, int y, int z); + + + /** + * Return the grid distance in between (aka 1-Norm, Minkowski or Manhattan distance) + * (x, y). + * + * @param v + * the other vector + * @return the grid distance + */ + long gridDistance(Vector3ic v); + + /** + * Return the grid distance in between (aka 1-Norm, Minkowski or Manhattan distance) + * (x, y). + * + * @param x + * the x component of the other vector + * @param y + * the y component of the other vector + * @param z + * the y component of the other vector + * @return the grid distance + */ + long gridDistance(int x, int y, int z); + + /** + * Return the square of the distance between this vector and v. + * + * @param v + * the other vector + * @return the squared of the distance + */ + long distanceSquared(Vector3ic v); + + /** + * Return the square of the distance between this vector and (x, y, z). + * + * @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 + * @return the square of the distance + */ + long distanceSquared(int x, int y, int z); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3i negate(Vector3i dest); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3i min(Vector3ic v, Vector3i dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector3i max(Vector3ic v, Vector3i dest); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..2] + * @return the value + * @throws IllegalArgumentException if component is not within [0..2] + */ + int get(int component) throws IllegalArgumentException; + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..2] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..2] + */ + int minComponent(); + + /** + * Compute the absolute of each of this vector's components + * and store the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector3i absolute(Vector3i dest); + + /** + * Compare the vector components of this vector with the given (x, y, z) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @param z + * the z component to compare to + * @return true if all the vector components are equal + */ + boolean equals(int x, int y, int z); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4d.java new file mode 100644 index 000000000..e19086c28 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4d.java @@ -0,0 +1,2129 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.*; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a Vector comprising 4 doubles and associated transformations. + * + * @author Richard Greenlees + * @author Kai Burjack + * @author F. Neurath + */ +public class Vector4d implements Externalizable, Cloneable, Vector4dc { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public double x; + /** + * The y component of the vector. + */ + public double y; + /** + * The z component of the vector. + */ + public double z; + /** + * The w component of the vector. + */ + public double w; + + /** + * Create a new {@link Vector4d} of (0, 0, 0, 1). + */ + public Vector4d() { + this.w = 1.0; + } + + /** + * Create a new {@link Vector4d} with the same values as v. + * + * @param v + * the {@link Vector4dc} to copy the values from + */ + public Vector4d(Vector4dc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + } + + /** + * Create a new {@link Vector4d} with the same values as v. + * + * @param v + * the {@link Vector4ic} to copy the values from + */ + public Vector4d(Vector4ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + } + + /** + * Create a new {@link Vector4d} with the first three components from the + * given v and the given w. + * + * @param v + * the {@link Vector3dc} + * @param w + * the w component + */ + public Vector4d(Vector3dc v, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + } + + /** + * Create a new {@link Vector4d} with the first three components from the + * given v and the given w. + * + * @param v + * the {@link Vector3ic} + * @param w + * the w component + */ + public Vector4d(Vector3ic v, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + } + + /** + * Create a new {@link Vector4d} with the first two components from the + * given v and the given z and w. + * + * @param v + * the {@link Vector2dc} + * @param z + * the z component + * @param w + * the w component + */ + public Vector4d(Vector2dc v, double z, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4d} with the first two components from the + * given v and the given z and w. + * + * @param v + * the {@link Vector2ic} + * @param z + * the z component + * @param w + * the w component + */ + public Vector4d(Vector2ic v, double z, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4d} and initialize all four components with the given value. + * + * @param d + * the value of all four components + */ + public Vector4d(double d) { + this.x = d; + this.y = d; + this.z = d; + this.w = d; + } + + /** + * Create a new {@link Vector4d} with the same values as v. + * + * @param v + * the {@link Vector4fc} to copy the values from + */ + public Vector4d(Vector4fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + } + + /** + * Create a new {@link Vector4d} with the x, y, and z components from the + * given v and the w component from the given w. + * + * @param v + * the {@link Vector3fc} + * @param w + * the w component + */ + public Vector4d(Vector3fc v, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + } + + /** + * Create a new {@link Vector4d} with the x and y components from the + * given v and the z and w components from the given z and w. + * + * @param v + * the {@link Vector2fc} + * @param z + * the z component + * @param w + * the w component + */ + public Vector4d(Vector2fc v, double z, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4d} with the given component values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @param w + * the w component + */ + public Vector4d(double x, double y, double z, double w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4d} and initialize its four components from the first + * four elements of the given array. + * + * @param xyzw + * the array containing at least four elements + */ + public Vector4d(float[] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + } + + /** + * Create a new {@link Vector4d} and initialize its four components from the first + * four elements of the given array. + * + * @param xyzw + * the array containing at least four elements + */ + public Vector4d(double[] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + } + + /** + * Create a new {@link Vector4d} and read this vector from the supplied {@link ByteBuffer} + * at the current buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #Vector4d(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z, w order + * @see #Vector4d(int, ByteBuffer) + */ + public Vector4d(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector4d} and read this vector from the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index the absolute position into the ByteBuffer + * @param buffer values will be read in x, y, z, w order + */ + public Vector4d(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector4d} and read this vector from the supplied {@link DoubleBuffer} + * at the current buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is read, use {@link #Vector4d(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer values will be read in x, y, z, w order + * @see #Vector4d(int, DoubleBuffer) + */ + public Vector4d(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector4d} and read this vector from the supplied {@link DoubleBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index the absolute position into the DoubleBuffer + * @param buffer values will be read in x, y, z, w order + */ + public Vector4d(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public double x() { + return this.x; + } + + public double y() { + return this.y; + } + + public double z() { + return this.z; + } + + public double w() { + return this.w; + } + + /** + * Set this {@link Vector4d} to the values of the given v. + * + * @param v + * the vector whose values will be copied into this + * @return this + */ + public Vector4d set(Vector4dc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + return this; + } + + /** + * Set this {@link Vector4d} to the values of the given v. + * + * @param v + * the vector whose values will be copied into this + * @return this + */ + public Vector4d set(Vector4fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + return this; + } + + /** + * Set this {@link Vector4d} to the values of the given v. + * + * @param v + * the vector whose values will be copied into this + * @return this + */ + public Vector4d set(Vector4ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + return this; + } + + /** + * Set the x, y, and z components of this to the components of + * v and the w component to w. + * + * @param v + * the {@link Vector3dc} to copy + * @param w + * the w component + * @return this + */ + public Vector4d set(Vector3dc v, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + return this; + } + + /** + * Set the x, y, and z components of this to the components of + * v and the w component to w. + * + * @param v + * the {@link Vector3ic} to copy + * @param w + * the w component + * @return this + */ + public Vector4d set(Vector3ic v, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + return this; + } + + /** + * Set the x, y, and z components of this to the components of + * v and the w component to w. + * + * @param v + * the {@link Vector3fc} to copy + * @param w + * the w component + * @return this + */ + public Vector4d set(Vector3fc v, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + return this; + } + + /** + * Set the x and y components from the given v + * and the z and w components to the given z and w. + * + * @param v + * the {@link Vector2dc} + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4d set(Vector2dc v, double z, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + return this; + } + + /** + * Set the x and y components from the given v + * and the z and w components to the given z and w. + * + * @param v + * the {@link Vector2ic} + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4d set(Vector2ic v, double z, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + return this; + } + + /** + * Set the x, y, z, and w components to the supplied value. + * + * @param d + * the value of all four components + * @return this + */ + public Vector4d set(double d) { + this.x = d; + this.y = d; + this.z = d; + this.w = d; + return this; + } + + /** + * Set the x and y components from the given v + * and the z and w components to the given z and w. + * + * @param v + * the {@link Vector2fc} + * @param z + * the z components + * @param w + * the w components + * @return this + */ + public Vector4d set(Vector2fc v, double z, double w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + return this; + } + + /** + * Set the x, y, z, and w components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4d set(double x, double y, double z, double w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + /** + * Set the x, y, z components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @return this + */ + public Vector4d set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + /** + * Set the four components of this vector to the first four elements of the given array. + * + * @param xyzw + * the array containing at least four elements + * @return this + */ + public Vector4d set(double[] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + return this; + } + + /** + * Set the four components of this vector to the first four elements of the given array. + * + * @param xyzw + * the array containing at least four elements + * @return this + */ + public Vector4d set(float[] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #set(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z, w order + * @return this + * @see #set(int, ByteBuffer) + */ + public Vector4d set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z, w order + * @return this + */ + public Vector4d set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is read, use {@link #set(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z, w order + * @return this + * @see #set(int, DoubleBuffer) + */ + public Vector4d set(DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * values will be read in x, y, z, w order + * @return this + */ + public Vector4d set(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 4 double values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector4d setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..3] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..3] + */ + public Vector4d setComponent(int component, double value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + case 3: + w = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public DoubleBuffer get(DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public DoubleBuffer get(int index, DoubleBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer getf(ByteBuffer buffer) { + MemUtil.INSTANCE.putf(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer getf(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.putf(this, index, buffer); + return buffer; + } + + public FloatBuffer get(FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector4dc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Subtract the supplied vector from this one. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector4d sub(Vector4dc v) { + this.x = x - v.x(); + this.y = y - v.y(); + this.z = z - v.z(); + this.w = w - v.w(); + return this; + } + + /** + * Subtract the supplied vector from this one and store the result in dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + public Vector4d sub(Vector4dc v, Vector4d dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + dest.z = z - v.z(); + dest.w = w - v.w(); + return dest; + } + + /** + * Subtract the supplied vector from this one. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector4d sub(Vector4fc v) { + this.x = x - v.x(); + this.y = y - v.y(); + this.z = z - v.z(); + this.w = w - v.w(); + return this; + } + + /** + * Subtract the supplied vector from this one and store the result in dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + public Vector4d sub(Vector4fc v, Vector4d dest) { + dest.x = x - v.x(); + dest.y = y - v.y(); + dest.z = z - v.z(); + dest.w = w - v.w(); + return dest; + } + + /** + * Subtract (x, y, z, w) from this. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param w + * the w component to subtract + * @return this + */ + public Vector4d sub(double x, double y, double z, double w) { + this.x = this.x - x; + this.y = this.y - y; + this.z = this.z - z; + this.w = this.w - w; + return this; + } + + public Vector4d sub(double x, double y, double z, double w, Vector4d dest) { + dest.x = this.x - x; + dest.y = this.y - y; + dest.z = this.z - z; + dest.w = this.w - w; + return dest; + } + + /** + * Add the supplied vector to this one. + * + * @param v + * the vector to add + * @return this + */ + public Vector4d add(Vector4dc v) { + this.x = x + v.x(); + this.y = y + v.y(); + this.z = z + v.z(); + this.w = w + v.w(); + return this; + } + + public Vector4d add(Vector4dc v, Vector4d dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + dest.z = z + v.z(); + dest.w = w + v.w(); + return dest; + } + + public Vector4d add(Vector4fc v, Vector4d dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + dest.z = z + v.z(); + dest.w = w + v.w(); + return dest; + } + + /** + * Add (x, y, z, w) to this. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @param w + * the w component to add + * @return this + */ + public Vector4d add(double x, double y, double z, double w) { + this.x = this.x + x; + this.y = this.y + y; + this.z = this.z + z; + this.w = this.w + w; + return this; + } + + public Vector4d add(double x, double y, double z, double w, Vector4d dest) { + dest.x = this.x + x; + dest.y = this.y + y; + dest.z = this.z + z; + dest.w = this.w + w; + return dest; + } + + /** + * Add the supplied vector to this one. + * + * @param v + * the vector to add + * @return this + */ + public Vector4d add(Vector4fc v) { + this.x = x + v.x(); + this.y = y + v.y(); + this.z = z + v.z(); + this.w = w + v.w(); + return this; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector4d fma(Vector4dc a, Vector4dc b) { + this.x = Math.fma(a.x(), b.x(), x); + this.y = Math.fma(a.y(), b.y(), y); + this.z = Math.fma(a.z(), b.z(), z); + this.w = Math.fma(a.w(), b.w(), w); + return this; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector4d fma(double a, Vector4dc b) { + this.x = Math.fma(a, b.x(), x); + this.y = Math.fma(a, b.y(), y); + this.z = Math.fma(a, b.z(), z); + this.w = Math.fma(a, b.w(), w); + return this; + } + + public Vector4d fma(Vector4dc a, Vector4dc b, Vector4d dest) { + dest.x = Math.fma(a.x(), b.x(), x); + dest.y = Math.fma(a.y(), b.y(), y); + dest.z = Math.fma(a.z(), b.z(), z); + dest.w = Math.fma(a.w(), b.w(), w); + return dest; + } + + public Vector4d fma(double a, Vector4dc b, Vector4d dest) { + dest.x = Math.fma(a, b.x(), x); + dest.y = Math.fma(a, b.y(), y); + dest.z = Math.fma(a, b.z(), z); + dest.w = Math.fma(a, b.w(), w); + return dest; + } + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in this. + * + * @param a + * the multiplicand + * @param b + * the addend + * @return this + */ + public Vector4d mulAdd(Vector4dc a, Vector4dc b) { + this.x = Math.fma(x, a.x(), b.x()); + this.y = Math.fma(y, a.y(), b.y()); + this.z = Math.fma(z, a.z(), b.z()); + return this; + } + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in this. + * + * @param a + * the multiplicand + * @param b + * the addend + * @return this + */ + public Vector4d mulAdd(double a, Vector4dc b) { + this.x = Math.fma(x, a, b.x()); + this.y = Math.fma(y, a, b.y()); + this.z = Math.fma(z, a, b.z()); + return this; + } + + public Vector4d mulAdd(Vector4dc a, Vector4dc b, Vector4d dest) { + dest.x = Math.fma(x, a.x(), b.x()); + dest.y = Math.fma(y, a.y(), b.y()); + dest.z = Math.fma(z, a.z(), b.z()); + return dest; + } + + public Vector4d mulAdd(double a, Vector4dc b, Vector4d dest) { + dest.x = Math.fma(x, a, b.x()); + dest.y = Math.fma(y, a, b.y()); + dest.z = Math.fma(z, a, b.z()); + return dest; + } + + /** + * Multiply this {@link Vector4d} component-wise by the given {@link Vector4d}. + * + * @param v + * the vector to multiply by + * @return this + */ + public Vector4d mul(Vector4dc v) { + this.x = x * v.x(); + this.y = y * v.y(); + this.z = z * v.z(); + this.w = w * v.w(); + return this; + } + + public Vector4d mul(Vector4dc v, Vector4d dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + dest.z = z * v.z(); + dest.w = w * v.w(); + return dest; + } + + /** + * Divide this {@link Vector4d} component-wise by the given {@link Vector4dc}. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector4d div(Vector4dc v) { + this.x = x / v.x(); + this.y = y / v.y(); + this.z = z / v.z(); + this.w = w / v.w(); + return this; + } + + public Vector4d div(Vector4dc v, Vector4d dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + dest.z = z / v.z(); + dest.w = w / v.w(); + return dest; + } + + /** + * Multiply this {@link Vector4d} component-wise by the given {@link Vector4fc}. + * + * @param v + * the vector to multiply by + * @return this + */ + public Vector4d mul(Vector4fc v) { + this.x = x * v.x(); + this.y = y * v.y(); + this.z = z * v.z(); + this.w = w * v.w(); + return this; + } + + public Vector4d mul(Vector4fc v, Vector4d dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + dest.z = z * v.z(); + dest.w = w * v.w(); + return dest; + } + + /** + * Multiply the given matrix mat with this {@link Vector4d}. + * + * @param mat + * the matrix to multiply by + * @return this + */ + public Vector4d mul(Matrix4dc mat) { + if ((mat.properties() & Matrix4fc.PROPERTY_AFFINE) != 0) + return mulAffine(mat, this); + return mulGeneric(mat, this); + } + + public Vector4d mul(Matrix4dc mat, Vector4d dest) { + if ((mat.properties() & Matrix4fc.PROPERTY_AFFINE) != 0) + return mulAffine(mat, dest); + return mulGeneric(mat, dest); + } + + /** + * Multiply the transpose of the given matrix mat with this Vector4f and store the result in + * this. + * + * @param mat + * the matrix whose transpose to multiply the vector with + * @return this + */ + public Vector4d mulTranspose(Matrix4dc mat) { + if ((mat.properties() & Matrix4dc.PROPERTY_AFFINE) != 0) + return mulAffineTranspose(mat, this); + return mulGenericTranspose(mat, this); + } + public Vector4d mulTranspose(Matrix4dc mat, Vector4d dest) { + if ((mat.properties() & Matrix4dc.PROPERTY_AFFINE) != 0) + return mulAffineTranspose(mat, dest); + return mulGenericTranspose(mat, dest); + } + + public Vector4d mulAffine(Matrix4dc mat, Vector4d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + dest.w = w; + return dest; + } + + private Vector4d mulGeneric(Matrix4dc mat, Vector4d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + double rw = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + dest.w = rw; + return dest; + } + + public Vector4d mulAffineTranspose(Matrix4dc mat, Vector4d dest) { + double x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + dest.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + dest.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + dest.w = Math.fma(mat.m30(), x, Math.fma(mat.m31(), y, mat.m32() * z + w)); + return dest; + } + private Vector4d mulGenericTranspose(Matrix4dc mat, Vector4d dest) { + double x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, Math.fma(mat.m02(), z, mat.m03() * w))); + dest.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, Math.fma(mat.m12(), z, mat.m13() * w))); + dest.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, Math.fma(mat.m22(), z, mat.m23() * w))); + dest.w = Math.fma(mat.m30(), x, Math.fma(mat.m31(), y, Math.fma(mat.m32(), z, mat.m33() * w))); + return dest; + } + + /** + * Multiply the given matrix mat with this Vector4d and store the result in + * this. + * + * @param mat + * the matrix to multiply the vector with + * @return this + */ + public Vector4d mul(Matrix4x3dc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector4d mul(Matrix4x3dc mat, Vector4d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + dest.w = w; + return dest; + } + + /** + * Multiply the given matrix mat with this Vector4d and store the result in + * this. + * + * @param mat + * the matrix to multiply the vector with + * @return this + */ + public Vector4d mul(Matrix4x3fc mat) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + this.x = rx; + this.y = ry; + this.z = rz; + return this; + } + + public Vector4d mul(Matrix4x3fc mat, Vector4d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + dest.w = w; + return dest; + } + + /** + * Multiply the given matrix mat with this {@link Vector4d}. + * + * @param mat + * the matrix to multiply by + * @return this + */ + public Vector4d mul(Matrix4fc mat) { + if ((mat.properties() & Matrix4fc.PROPERTY_AFFINE) != 0) + return mulAffine(mat, this); + return mulGeneric(mat, this); + } + + public Vector4d mul(Matrix4fc mat, Vector4d dest) { + if ((mat.properties() & Matrix4fc.PROPERTY_AFFINE) != 0) + return mulAffine(mat, dest); + return mulGeneric(mat, dest); + } + private Vector4d mulAffine(Matrix4fc mat, Vector4d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + dest.w = w; + return dest; + } + private Vector4d mulGeneric(Matrix4fc mat, Vector4d dest) { + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + double rw = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + dest.x = rx; + dest.y = ry; + dest.z = rz; + dest.w = rw; + return dest; + } + + public Vector4d mulProject(Matrix4dc mat, Vector4d dest) { + double invW = 1.0 / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))) * invW; + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))) * invW; + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))) * invW; + dest.x = rx; + dest.y = ry; + dest.z = rz; + dest.w = 1.0; + return dest; + } + + /** + * Multiply the given matrix mat with this Vector4d, perform perspective division. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector4d mulProject(Matrix4dc mat) { + double invW = 1.0 / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))) * invW; + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))) * invW; + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))) * invW; + this.x = rx; + this.y = ry; + this.z = rz; + this.w = 1.0; + return this; + } + + public Vector3d mulProject(Matrix4dc mat, Vector3d dest) { + double invW = 1.0 / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + double rx = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))) * invW; + double ry = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))) * invW; + double rz = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))) * invW; + dest.x = rx; + dest.y = ry; + dest.z = rz; + return dest; + } + + /** + * Multiply this Vector4d by the given scalar value. + * + * @param scalar + * the scalar to multiply by + * @return this + */ + public Vector4d mul(double scalar) { + this.x = x * scalar; + this.y = y * scalar; + this.z = z * scalar; + this.w = w * scalar; + return this; + } + + public Vector4d mul(double scalar, Vector4d dest) { + dest.x = x * scalar; + dest.y = y * scalar; + dest.z = z * scalar; + dest.w = w * scalar; + return dest; + } + + /** + * Divide this Vector4d by the given scalar value. + * + * @param scalar + * the scalar to divide by + * @return this + */ + public Vector4d div(double scalar) { + double inv = 1.0 / scalar; + this.x = x * inv; + this.y = y * inv; + this.z = z * inv; + this.w = w * inv; + return this; + } + + public Vector4d div(double scalar, Vector4d dest) { + double inv = 1.0 / scalar; + dest.x = x * inv; + dest.y = y * inv; + dest.z = z * inv; + dest.w = w * inv; + return dest; + } + + /** + * Transform this vector by the given quaternion quat and store the result in this. + * + * @see Quaterniond#transform(Vector4d) + * + * @param quat + * the quaternion to transform this vector + * @return this + */ + public Vector4d rotate(Quaterniondc quat) { + quat.transform(this, this); + return this; + } + + public Vector4d rotate(Quaterniondc quat, Vector4d dest) { + quat.transform(this, dest); + return dest; + } + + /** + * Rotate this vector the specified radians around the given rotation axis. + * + * @param angle + * the angle in radians + * @param x + * the x component of the rotation axis + * @param y + * the y component of the rotation axis + * @param z + * the z component of the rotation axis + * @return this + */ + public Vector4d rotateAxis(double angle, double x, double y, double z) { + if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x)) + return rotateX(x * angle, this); + else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y)) + return rotateY(y * angle, this); + else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z)) + return rotateZ(z * angle, this); + return rotateAxisInternal(angle, x, y, z, this); + } + + public Vector4d rotateAxis(double angle, double aX, double aY, double aZ, Vector4d dest) { + if (aY == 0.0 && aZ == 0.0 && Math.absEqualsOne(aX)) + return rotateX(aX * angle, dest); + else if (aX == 0.0 && aZ == 0.0 && Math.absEqualsOne(aY)) + return rotateY(aY * angle, dest); + else if (aX == 0.0 && aY == 0.0 && Math.absEqualsOne(aZ)) + return rotateZ(aZ * angle, dest); + return rotateAxisInternal(angle, aX, aY, aZ, dest); + } + private Vector4d rotateAxisInternal(double angle, double aX, double aY, double aZ, Vector4d dest) { + double hangle = angle * 0.5; + double sinAngle = Math.sin(hangle); + double qx = aX * sinAngle, qy = aY * sinAngle, qz = aZ * sinAngle; + double qw = Math.cosFromSin(sinAngle, hangle); + double w2 = qw * qw, x2 = qx * qx, y2 = qy * qy, z2 = qz * qz, zw = qz * qw; + double xy = qx * qy, xz = qx * qz, yw = qy * qw, yz = qy * qz, xw = qx * qw; + double nx = (w2 + x2 - z2 - y2) * x + (-zw + xy - zw + xy) * y + (yw + xz + xz + yw) * z; + double ny = (xy + zw + zw + xy) * x + ( y2 - z2 + w2 - x2) * y + (yz + yz - xw - xw) * z; + double nz = (xz - yw + xz - yw) * x + ( yz + yz + xw + xw) * y + (z2 - y2 - x2 + w2) * z; + dest.x = nx; + dest.y = ny; + dest.z = nz; + return dest; + } + + /** + * Rotate this vector the specified radians around the X axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector4d rotateX(double angle) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double y = this.y * cos - this.z * sin; + double z = this.y * sin + this.z * cos; + this.y = y; + this.z = z; + return this; + } + + public Vector4d rotateX(double angle, Vector4d dest) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double y = this.y * cos - this.z * sin; + double z = this.y * sin + this.z * cos; + dest.x = this.x; + dest.y = y; + dest.z = z; + dest.w = this.w; + return dest; + } + + /** + * Rotate this vector the specified radians around the Y axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector4d rotateY(double angle) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double x = this.x * cos + this.z * sin; + double z = -this.x * sin + this.z * cos; + this.x = x; + this.z = z; + return this; + } + + public Vector4d rotateY(double angle, Vector4d dest) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double x = this.x * cos + this.z * sin; + double z = -this.x * sin + this.z * cos; + dest.x = x; + dest.y = this.y; + dest.z = z; + dest.w = this.w; + return dest; + } + + /** + * Rotate this vector the specified radians around the Z axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector4d rotateZ(double angle) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double x = this.x * cos - this.y * sin; + double y = this.x * sin + this.y * cos; + this.x = x; + this.y = y; + return this; + } + + public Vector4d rotateZ(double angle, Vector4d dest) { + double sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + double x = this.x * cos - this.y * sin; + double y = this.x * sin + this.y * cos; + dest.x = x; + dest.y = y; + dest.z = this.z; + dest.w = this.w; + return dest; + } + + public double lengthSquared() { + return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + } + + /** + * Get the length squared of a 4-dimensional double-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * @param w The vector's w component + * + * @return the length squared of the given vector + * + * @author F. Neurath + */ + public static double lengthSquared(double x, double y, double z, double w) { + return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + } + + public double length() { + return Math.sqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + } + + /** + * Get the length of a 4-dimensional double-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * @param w The vector's w component + * + * @return the length of the given vector + * + * @author F. Neurath + */ + public static double length(double x, double y, double z, double w) { + return Math.sqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + } + + /** + * Normalizes this vector. + * + * @return this + */ + public Vector4d normalize() { + double invLength = 1.0 / length(); + this.x = x * invLength; + this.y = y * invLength; + this.z = z * invLength; + this.w = w * invLength; + return this; + } + + public Vector4d normalize(Vector4d dest) { + double invLength = 1.0 / length(); + dest.x = x * invLength; + dest.y = y * invLength; + dest.z = z * invLength; + dest.w = w * invLength; + return dest; + } + + /** + * Scale this vector to have the given length. + * + * @param length + * the desired length + * @return this + */ + public Vector4d normalize(double length) { + double invLength = 1.0 / length() * length; + this.x = x * invLength; + this.y = y * invLength; + this.z = z * invLength; + this.w = w * invLength; + return this; + } + + public Vector4d normalize(double length, Vector4d dest) { + double invLength = 1.0 / length() * length; + dest.x = x * invLength; + dest.y = y * invLength; + dest.z = z * invLength; + dest.w = w * invLength; + return dest; + } + + /** + * Normalize this vector by computing only the norm of (x, y, z). + * + * @return this + */ + public Vector4d normalize3() { + double invLength = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + this.x = x * invLength; + this.y = y * invLength; + this.z = z * invLength; + this.w = w * invLength; + return this; + } + + public Vector4d normalize3(Vector4d dest) { + double invLength = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + dest.x = x * invLength; + dest.y = y * invLength; + dest.z = z * invLength; + dest.w = w * invLength; + return dest; + } + + public double distance(Vector4dc v) { + double dx = this.x - v.x(); + double dy = this.y - v.y(); + double dz = this.z - v.z(); + double dw = this.w - v.w(); + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw)))); + } + + public double distance(double x, double y, double z, double w) { + double dx = this.x - x; + double dy = this.y - y; + double dz = this.z - z; + double dw = this.w - w; + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw)))); + } + + public double distanceSquared(Vector4dc v) { + double dx = this.x - v.x(); + double dy = this.y - v.y(); + double dz = this.z - v.z(); + double dw = this.w - v.w(); + return Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw))); + } + + public double distanceSquared(double x, double y, double z, double w) { + double dx = this.x - x; + double dy = this.y - y; + double dz = this.z - z; + double dw = this.w - w; + return Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw))); + } + + /** + * Return the distance between (x1, y1, z1, w1) and (x2, y2, z2, w2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param w1 + * the w component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @param w2 + * the 2 component of the second vector + * @return the euclidean distance + */ + public static double distance(double x1, double y1, double z1, double w1, double x2, double y2, double z2, double w2) { + double dx = x1 - x2; + double dy = y1 - y2; + double dz = z1 - z2; + double dw = w1 - w2; + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw)))); + } + + /** + * Return the squared distance between (x1, y1, z1, w1) and (x2, y2, z2, w2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param w1 + * the w component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @param w2 + * the w component of the second vector + * @return the euclidean distance squared + */ + public static double distanceSquared(double x1, double y1, double z1, double w1, double x2, double y2, double z2, double w2) { + double dx = x1 - x2; + double dy = y1 - y2; + double dz = z1 - z2; + double dw = w1 - w2; + return Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw))); + } + + public double dot(Vector4dc v) { + return Math.fma(this.x, v.x(), Math.fma(this.y, v.y(), Math.fma(this.z, v.z(), this.w * v.w()))); + } + + public double dot(double x, double y, double z, double w) { + return Math.fma(this.x, x, Math.fma(this.y, y, Math.fma(this.z, z, this.w * w))); + } + + public double angleCos(Vector4dc v) { + double length1Squared = Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + double length2Squared = Math.fma(v.x(), v.x(), Math.fma(v.y(), v.y(), Math.fma(v.z(), v.z(), v.w() * v.w()))); + double dot = Math.fma(x, v.x(), Math.fma(y, v.y(), Math.fma(z, v.z(), w * v.w()))); + return dot / Math.sqrt(length1Squared * length2Squared); + } + + public double angle(Vector4dc v) { + double cos = angleCos(v); + // This is because sometimes cos goes above 1 or below -1 because of lost precision + cos = cos < 1 ? cos : 1; + cos = cos > -1 ? cos : -1; + return Math.acos(cos); + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector4d zero() { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + return this; + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector4d negate() { + this.x = -x; + this.y = -y; + this.z = -z; + this.w = -w; + return this; + } + + public Vector4d negate(Vector4d dest) { + dest.x = -x; + dest.y = -y; + dest.z = -z; + dest.w = -w; + return dest; + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector4d min(Vector4dc v) { + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + this.z = z < v.z() ? z : v.z(); + this.w = w < v.w() ? w : v.w(); + return this; + } + + public Vector4d min(Vector4dc v, Vector4d dest) { + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + dest.z = z < v.z() ? z : v.z(); + dest.w = w < v.w() ? w : v.w(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector4d max(Vector4dc v) { + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + this.z = z > v.z() ? z : v.z(); + this.w = w > v.w() ? w : v.w(); + return this; + } + + public Vector4d max(Vector4dc v, Vector4d dest) { + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + dest.z = z > v.z() ? z : v.z(); + dest.w = w > v.w() ? w : v.w(); + return dest; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + " " + Runtime.format(z, formatter) + " " + Runtime.format(w, formatter) + ")"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeDouble(x); + out.writeDouble(y); + out.writeDouble(z); + out.writeDouble(w); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + x = in.readDouble(); + y = in.readDouble(); + z = in.readDouble(); + w = in.readDouble(); + } + + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(w); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(x); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(y); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(z); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector4d other = (Vector4d) obj; + if (Double.doubleToLongBits(w) != Double.doubleToLongBits(other.w)) + return false; + if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) + return false; + if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) + return false; + if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z)) + return false; + return true; + } + + public boolean equals(Vector4dc v, double delta) { + if (this == v) + return true; + if (v == null) + return false; + if (!(v instanceof Vector4dc)) + return false; + if (!Runtime.equals(x, v.x(), delta)) + return false; + if (!Runtime.equals(y, v.y(), delta)) + return false; + if (!Runtime.equals(z, v.z(), delta)) + return false; + if (!Runtime.equals(w, v.w(), delta)) + return false; + return true; + } + + public boolean equals(double x, double y, double z, double w) { + if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(x)) + return false; + if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(y)) + return false; + if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(z)) + return false; + if (Double.doubleToLongBits(this.w) != Double.doubleToLongBits(w)) + return false; + return true; + } + + public Vector4d smoothStep(Vector4dc v, double t, Vector4d dest) { + double t2 = t * t; + double t3 = t2 * t; + dest.x = (x + x - v.x() - v.x()) * t3 + (3.0 * v.x() - 3.0 * x) * t2 + x * t + x; + dest.y = (y + y - v.y() - v.y()) * t3 + (3.0 * v.y() - 3.0 * y) * t2 + y * t + y; + dest.z = (z + z - v.z() - v.z()) * t3 + (3.0 * v.z() - 3.0 * z) * t2 + z * t + z; + dest.w = (w + w - v.w() - v.w()) * t3 + (3.0 * v.w() - 3.0 * w) * t2 + w * t + w; + return dest; + } + + public Vector4d hermite(Vector4dc t0, Vector4dc v1, Vector4dc t1, double t, Vector4d dest) { + double t2 = t * t; + double t3 = t2 * t; + dest.x = (x + x - v1.x() - v1.x() + t1.x() + t0.x()) * t3 + (3.0 * v1.x() - 3.0 * x - t0.x() - t0.x() - t1.x()) * t2 + x * t + x; + dest.y = (y + y - v1.y() - v1.y() + t1.y() + t0.y()) * t3 + (3.0 * v1.y() - 3.0 * y - t0.y() - t0.y() - t1.y()) * t2 + y * t + y; + dest.z = (z + z - v1.z() - v1.z() + t1.z() + t0.z()) * t3 + (3.0 * v1.z() - 3.0 * z - t0.z() - t0.z() - t1.z()) * t2 + z * t + z; + dest.w = (w + w - v1.w() - v1.w() + t1.w() + t0.w()) * t3 + (3.0 * v1.w() - 3.0 * w - t0.w() - t0.w() - t1.w()) * t2 + w * t + w; + return dest; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Vector4d lerp(Vector4dc other, double t) { + this.x = Math.fma(other.x() - x, t, x); + this.y = Math.fma(other.y() - y, t, y); + this.z = Math.fma(other.z() - z, t, z); + this.w = Math.fma(other.w() - w, t, w); + return this; + } + + public Vector4d lerp(Vector4dc other, double t, Vector4d dest) { + dest.x = Math.fma(other.x() - x, t, x); + dest.y = Math.fma(other.y() - y, t, y); + dest.z = Math.fma(other.z() - z, t, z); + dest.w = Math.fma(other.w() - w, t, w); + return dest; + } + + public double get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IllegalArgumentException(); + } + } + + public Vector4i get(int mode, Vector4i dest) { + dest.x = Math.roundUsing(this.x(), mode); + dest.y = Math.roundUsing(this.y(), mode); + dest.z = Math.roundUsing(this.z(), mode); + dest.w = Math.roundUsing(this.w(), mode); + return dest; + } + + public Vector4f get(Vector4f dest) { + dest.x = (float) this.x(); + dest.y = (float) this.y(); + dest.z = (float) this.z(); + dest.w = (float) this.w(); + return dest; + } + + public Vector4d get(Vector4d dest) { + dest.x = this.x(); + dest.y = this.y(); + dest.z = this.z(); + dest.w = this.w(); + return dest; + } + + public int maxComponent() { + double absX = Math.abs(x); + double absY = Math.abs(y); + double absZ = Math.abs(z); + double absW = Math.abs(w); + if (absX >= absY && absX >= absZ && absX >= absW) { + return 0; + } else if (absY >= absZ && absY >= absW) { + return 1; + } else if (absZ >= absW) { + return 2; + } + return 3; + } + + public int minComponent() { + double absX = Math.abs(x); + double absY = Math.abs(y); + double absZ = Math.abs(z); + double absW = Math.abs(w); + if (absX < absY && absX < absZ && absX < absW) { + return 0; + } else if (absY < absZ && absY < absW) { + return 1; + } else if (absZ < absW) { + return 2; + } + return 3; + } + + /** + * Set each component of this vector to the largest (closest to positive + * infinity) {@code double} value that is less than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector4d floor() { + this.x = Math.floor(x); + this.y = Math.floor(y); + this.z = Math.floor(z); + this.w = Math.floor(w); + return this; + } + + public Vector4d floor(Vector4d dest) { + dest.x = Math.floor(x); + dest.y = Math.floor(y); + dest.z = Math.floor(z); + dest.w = Math.floor(w); + return dest; + } + + /** + * Set each component of this vector to the smallest (closest to negative + * infinity) {@code double} value that is greater than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector4d ceil() { + this.x = Math.ceil(x); + this.y = Math.ceil(y); + this.z = Math.ceil(z); + this.w = Math.ceil(w); + return this; + } + + public Vector4d ceil(Vector4d dest) { + dest.x = Math.ceil(x); + dest.y = Math.ceil(y); + dest.z = Math.ceil(z); + dest.w = Math.ceil(w); + return dest; + } + + /** + * Set each component of this vector to the closest double that is equal to + * a mathematical integer, with ties rounding to positive infinity. + * + * @return this + */ + public Vector4d round() { + this.x = Math.round(x); + this.y = Math.round(y); + this.z = Math.round(z); + this.w = Math.round(w); + return this; + } + + public Vector4d round(Vector4d dest) { + dest.x = Math.round(x); + dest.y = Math.round(y); + dest.z = Math.round(z); + dest.w = Math.round(w); + return dest; + } + + public boolean isFinite() { + return Math.isFinite(x) && Math.isFinite(y) && Math.isFinite(z) && Math.isFinite(w); + } + + /** + * Compute the absolute of each of this vector's components. + * + * @return this + */ + public Vector4d absolute() { + this.x = Math.abs(x); + this.y = Math.abs(y); + this.z = Math.abs(z); + this.w = Math.abs(w); + return this; + } + + public Vector4d absolute(Vector4d dest) { + dest.x = Math.abs(x); + dest.y = Math.abs(y); + dest.z = Math.abs(z); + dest.w = Math.abs(w); + return dest; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4dc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4dc.java new file mode 100644 index 000000000..d485096d9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4dc.java @@ -0,0 +1,933 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 4-dimensional vector of double-precision floats. + * + * @author Kai Burjack + */ +public interface Vector4dc { + + /** + * @return the value of the x component + */ + double x(); + + /** + * @return the value of the y component + */ + double y(); + + /** + * @return the value of the z component + */ + double z(); + + /** + * @return the value of the w component + */ + double w(); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + * @see #get(int, ByteBuffer) + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link DoubleBuffer} at the current + * buffer {@link DoubleBuffer#position() position}. + *

+ * This method will not increment the position of the given DoubleBuffer. + *

+ * In order to specify the offset into the DoubleBuffer at which + * the vector is stored, use {@link #get(int, DoubleBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + * @see #get(int, DoubleBuffer) + */ + DoubleBuffer get(DoubleBuffer buffer); + + /** + * Store this vector into the supplied {@link DoubleBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given DoubleBuffer. + * + * @param index + * the absolute position into the DoubleBuffer + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + DoubleBuffer get(int index, DoubleBuffer buffer); + + /** + * Store this vector into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + *

+ * Please note that due to this vector storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + * @see #get(int, DoubleBuffer) + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * Please note that due to this vector storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + *

+ * Please note that due to this vector storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + * + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + * @see #get(int, ByteBuffer) + */ + ByteBuffer getf(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * Please note that due to this vector storing double values those values will potentially + * lose precision when they are converted to float values before being put into the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + ByteBuffer getf(int index, ByteBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector4dc getToAddress(long address); + + /** + * Subtract the supplied vector from this one and store the result in dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector4d sub(Vector4dc v, Vector4d dest); + + /** + * Subtract the supplied vector from this one and store the result in dest. + * + * @param v + * the vector to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector4d sub(Vector4fc v, Vector4d dest); + + /** + * Subtract (x, y, z, w) from this and store the result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param w + * the w component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector4d sub(double x, double y, double z, double w, Vector4d dest); + + /** + * Add the supplied vector to this one and store the result in dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector4d add(Vector4dc v, Vector4d dest); + + /** + * Add the supplied vector to this one and store the result in dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector4d add(Vector4fc v, Vector4d dest); + + /** + * Add (x, y, z, w) to this and store the result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param w + * the w component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector4d add(double x, double y, double z, double w, Vector4d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector4d fma(Vector4dc a, Vector4dc b, Vector4d dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector4d fma(double a, Vector4dc b, Vector4d dest); + + /** + * Multiply this {@link Vector4d} component-wise by the given {@link Vector4dc} and store the result in dest. + * + * @param v + * the vector to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Vector4d mul(Vector4dc v, Vector4d dest); + + /** + * Multiply this {@link Vector4d} component-wise by the given {@link Vector4fc} and store the result in dest. + * + * @param v + * the vector to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Vector4d mul(Vector4fc v, Vector4d dest); + + /** + * Divide this {@link Vector4d} component-wise by the given {@link Vector4dc} and store the result in dest. + * + * @param v + * the vector to divide this by + * @param dest + * will hold the result + * @return dest + */ + Vector4d div(Vector4dc v, Vector4d dest); + + /** + * Multiply the given matrix mat with this {@link Vector4d} and store the result in dest. + * + * @param mat + * the matrix to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Vector4d mul(Matrix4dc mat, Vector4d dest); + + /** + * Multiply the given matrix mat with this Vector4d and store the result in + * dest. + * + * @param mat + * the matrix to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4d mul(Matrix4x3dc mat, Vector4d dest); + + /** + * Multiply the given matrix mat with this Vector4d and store the result in + * dest. + * + * @param mat + * the matrix to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4d mul(Matrix4x3fc mat, Vector4d dest); + + /** + * Multiply the given matrix mat with this Vector4d and store the result in dest. + * + * @param mat + * the matrix to multiply this by + * @param dest + * will hold the result + * @return dest + */ + Vector4d mul(Matrix4fc mat, Vector4d dest); + + /** + * Multiply the transpose of the given matrix mat with this Vector4d and store the result in + * dest. + * + * @param mat + * the matrix whose transpose to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4d mulTranspose(Matrix4dc mat, Vector4d dest); + + /** + * Multiply the given affine matrix mat with this Vector4d and store the result in + * dest. + * + * @param mat + * the affine matrix to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4d mulAffine(Matrix4dc mat, Vector4d dest); + + /** + * Multiply the transpose of the given affine matrix mat with this Vector4d and store the result in + * dest. + * + * @param mat + * the affine matrix whose transpose to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4d mulAffineTranspose(Matrix4dc mat, Vector4d dest); + + /** + * Multiply the given matrix mat with this Vector4d, perform perspective division + * and store the result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector4d mulProject(Matrix4dc mat, Vector4d dest); + + /** + * Multiply the given matrix mat with this Vector4d, perform perspective division + * and store the (x, y, z) result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3d mulProject(Matrix4dc mat, Vector3d dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector4d mulAdd(Vector4dc a, Vector4dc b, Vector4d dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector4d mulAdd(double a, Vector4dc b, Vector4d dest); + + /** + * Multiply this Vector4d by the given scalar value and store the result in dest. + * + * @param scalar + * the factor to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector4d mul(double scalar, Vector4d dest); + + /** + * Divide this Vector4d by the given scalar value and store the result in dest. + * + * @param scalar + * the factor to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector4d div(double scalar, Vector4d dest); + + /** + * Transform this vector by the given quaternion quat and store the result in dest. + * + * @see Quaterniond#transform(Vector4d) + * + * @param quat + * the quaternion to transform this vector + * @param dest + * will hold the result + * @return dest + */ + Vector4d rotate(Quaterniondc quat, Vector4d dest); + + /** + * Rotate this vector the specified radians around the given rotation axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param aX + * the x component of the rotation axis + * @param aY + * the y component of the rotation axis + * @param aZ + * the z component of the rotation axis + * @param dest + * will hold the result + * @return dest + */ + Vector4d rotateAxis(double angle, double aX, double aY, double aZ, Vector4d dest); + + /** + * Rotate this vector the specified radians around the X axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector4d rotateX(double angle, Vector4d dest); + + /** + * Rotate this vector the specified radians around the Y axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector4d rotateY(double angle, Vector4d dest); + + /** + * Rotate this vector the specified radians around the Z axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector4d rotateZ(double angle, Vector4d dest); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + double lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + double length(); + + /** + * Normalizes this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d normalize(Vector4d dest); + + /** + * Scale this vector to have the given length and store the result in dest. + * + * @param length + * the desired length + * @param dest + * will hold the result + * @return dest + */ + Vector4d normalize(double length, Vector4d dest); + + /** + * Normalize this vector by computing only the norm of (x, y, z) and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d normalize3(Vector4d dest); + + /** + * Return the distance between this Vector and v. + * + * @param v + * the other vector + * @return the distance + */ + double distance(Vector4dc v); + + /** + * Return the distance between this vector and (x, y, z, w). + * + * @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 euclidean distance + */ + double distance(double x, double y, double z, double w); + + /** + * Return the square of the distance between this vector and v. + * + * @param v + * the other vector + * @return the squared of the distance + */ + double distanceSquared(Vector4dc v); + + /** + * Return the square of the distance between this vector and + * (x, y, z, w). + * + * @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 square of the distance + */ + double distanceSquared(double x, double y, double z, double w); + + /** + * Compute the dot product (inner product) of this vector and v. + * + * @param v + * the other vector + * @return the dot product + */ + double dot(Vector4dc v); + + /** + * Compute the dot product (inner product) of this vector and (x, y, z, w). + * + * @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 + */ + double dot(double x, double y, double z, double w); + + /** + * Return the cosine of the angle between this vector and the supplied vector. + *

+ * Use this instead of Math.cos(angle(v)). + * + * @see #angle(Vector4dc) + * + * @param v + * the other vector + * @return the cosine of the angle + */ + double angleCos(Vector4dc v); + + /** + * Return the angle between this vector and the supplied vector. + * + * @see #angleCos(Vector4dc) + * + * @param v + * the other vector + * @return the angle, in radians + */ + double angle(Vector4dc v); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d negate(Vector4d dest); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector4d min(Vector4dc v, Vector4d dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector4d max(Vector4dc v, Vector4d dest); + + /** + * Compute a smooth-step (i.e. hermite with zero tangents) interpolation + * between this vector and the given vector v and + * store the result in dest. + * + * @param v + * the other vector + * @param t + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Vector4d smoothStep(Vector4dc v, double t, Vector4d dest); + + /** + * Compute a hermite interpolation between this vector and its + * associated tangent t0 and the given vector v + * with its tangent t1 and store the result in + * dest. + * + * @param t0 + * the tangent of this vector + * @param v1 + * the other vector + * @param t1 + * the tangent of the other vector + * @param t + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Vector4d hermite(Vector4dc t0, Vector4dc v1, Vector4dc t1, double t, Vector4d dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Vector4d lerp(Vector4dc other, double t, Vector4d dest); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..3] + * @return the value + * @throws IllegalArgumentException if component is not within [0..3] + */ + double get(int component) throws IllegalArgumentException; + + /** + * Set the components of the given vector dest to those of this vector + * using the given {@link RoundingMode}. + * + * @param mode + * the {@link RoundingMode} to use + * @param dest + * will hold the result + * @return dest + */ + Vector4i get(int mode, Vector4i dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f get(Vector4f dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d get(Vector4d dest); + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..3] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..3] + */ + int minComponent(); + + /** + * Compute for each component of this vector the largest (closest to positive + * infinity) {@code double} value that is less than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d floor(Vector4d dest); + + /** + * Compute for each component of this vector the smallest (closest to negative + * infinity) {@code double} value that is greater than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d ceil(Vector4d dest); + + /** + * Compute for each component of this vector the closest double that is equal to + * a mathematical integer, with ties rounding to positive infinity and store + * the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d round(Vector4d dest); + + /** + * Determine whether all components are finite floating-point values, that + * is, they are not {@link Double#isNaN() NaN} and not + * {@link Double#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + * Compute the absolute of each of this vector's components + * and store the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d absolute(Vector4d dest); + + /** + * Compare the vector components of this vector with the given vector using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param v + * the other vector + * @param delta + * the allowed maximum difference + * @return true whether all of the vector components are equal; false otherwise + */ + boolean equals(Vector4dc v, double delta); + + /** + * Compare the vector components of this vector with the given (x, y, z, w) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @param z + * the z component to compare to + * @param w + * the w component to compare to + * @return true if all the vector components are equal + */ + boolean equals(double x, double y, double z, double w); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4f.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4f.java new file mode 100644 index 000000000..185ab4ca3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4f.java @@ -0,0 +1,1939 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a Vector comprising 4 floats and associated + * transformations. + * + * @author Richard Greenlees + * @author Kai Burjack + * @author F. Neurath + */ +public class Vector4f implements Externalizable, Cloneable, Vector4fc { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public float x; + /** + * The y component of the vector. + */ + public float y; + /** + * The z component of the vector. + */ + public float z; + /** + * The w component of the vector. + */ + public float w; + + /** + * Create a new {@link Vector4f} of (0, 0, 0, 1). + */ + public Vector4f() { + this.w = 1.0f; + } + + /** + * Create a new {@link Vector4f} with the same values as v. + * + * @param v + * the {@link Vector4fc} to copy the values from + */ + public Vector4f(Vector4fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + } + + /** + * Create a new {@link Vector4f} with the same values as v. + * + * @param v + * the {@link Vector4ic} to copy the values from + */ + public Vector4f(Vector4ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + } + + /** + * Create a new {@link Vector4f} with the first three components from the + * given v and the given w. + * + * @param v + * the {@link Vector3fc} + * @param w + * the w component + */ + public Vector4f(Vector3fc v, float w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + } + + /** + * Create a new {@link Vector4f} with the first three components from the + * given v and the given w. + * + * @param v + * the {@link Vector3ic} + * @param w + * the w component + */ + public Vector4f(Vector3ic v, float w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + } + + /** + * Create a new {@link Vector4f} with the first two components from the + * given v and the given z, and w. + * + * @param v + * the {@link Vector2fc} + * @param z + * the z component + * @param w + * the w component + */ + public Vector4f(Vector2fc v, float z, float w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4f} with the first two components from the + * given v and the given z, and w. + * + * @param v + * the {@link Vector2ic} + * @param z + * the z component + * @param w + * the w component + */ + public Vector4f(Vector2ic v, float z, float w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4f} and initialize all four components with the given value. + * + * @param d + * the value of all four components + */ + public Vector4f(float d) { + this.x = d; + this.y = d; + this.z = d; + this.w = d; + } + + /** + * Create a new {@link Vector4f} with the given component values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @param w + * the w component + */ + public Vector4f(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4f} and initialize its four components from the first + * four elements of the given array. + * + * @param xyzw + * the array containing at least four elements + */ + public Vector4f(float[] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + } + + /** + * Create a new {@link Vector4f} and read this vector from the supplied {@link ByteBuffer} + * at the current buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #Vector4f(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z, w order + * @see #Vector4f(int, ByteBuffer) + */ + public Vector4f(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector4f} and read this vector from the supplied {@link ByteBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z, w order + */ + public Vector4f(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector4f} and read this vector from the supplied {@link FloatBuffer} + * at the current buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is read, use {@link #Vector4f(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z, w order + * @see #Vector4f(int, FloatBuffer) + */ + public Vector4f(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector4f} and read this vector from the supplied {@link FloatBuffer} + * starting at the specified absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * values will be read in x, y, z, w order + */ + public Vector4f(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public float x() { + return this.x; + } + + public float y() { + return this.y; + } + + public float z() { + return this.z; + } + + public float w() { + return this.w; + } + + /** + * Set this {@link Vector4f} to the values of the given v. + * + * @param v + * the vector whose values will be copied into this + * @return this + */ + public Vector4f set(Vector4fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + return this; + } + + /** + * Set this {@link Vector4f} to the values of the given v. + * + * @param v + * the vector whose values will be copied into this + * @return this + */ + public Vector4f set(Vector4ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + return this; + } + + /** + * Set this {@link Vector4f} to the values of the given v. + *

+ * Note that due to the given vector v storing the components in double-precision, + * there is the possibility to lose precision. + * + * @param v + * the vector whose values will be copied into this + * @return this + */ + public Vector4f set(Vector4dc v) { + this.x = (float) v.x(); + this.y = (float) v.y(); + this.z = (float) v.z(); + this.w = (float) v.w(); + return this; + } + + /** + * Set the first three components of this to the components of + * v and the last component to w. + * + * @param v + * the {@link Vector3fc} to copy + * @param w + * the w component + * @return this + */ + public Vector4f set(Vector3fc v, float w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + return this; + } + + /** + * Set the first three components of this to the components of + * v and the last component to w. + * + * @param v + * the {@link Vector3ic} to copy + * @param w + * the w component + * @return this + */ + public Vector4f set(Vector3ic v, float w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + return this; + } + + /** + * Sets the first two components of this to the components of given v + * and last two components to the given z, and w. + * + * @param v + * the {@link Vector2fc} + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4f set(Vector2fc v, float z, float w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + return this; + } + + /** + * Sets the first two components of this to the components of given v + * and last two components to the given z, and w. + * + * @param v + * the {@link Vector2ic} + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4f set(Vector2ic v, float z, float w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + return this; + } + + /** + * Set the x, y, z, and w components to the supplied value. + * + * @param d + * the value of all four components + * @return this + */ + public Vector4f set(float d) { + this.x = d; + this.y = d; + this.z = d; + this.w = d; + return this; + } + + /** + * Set the x, y, z, and w components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4f set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + /** + * Set the x, y, z components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @return this + */ + public Vector4f set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + /** + * Set the x, y, z, and w components to the supplied value. + * + * @param d + * the value of all four components + * @return this + */ + public Vector4f set(double d) { + this.x = (float) d; + this.y = (float) d; + this.z = (float) d; + this.w = (float) d; + return this; + } + + /** + * Set the x, y, z, and w components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4f set(double x, double y, double z, double w) { + this.x = (float) x; + this.y = (float) y; + this.z = (float) z; + this.w = (float) w; + return this; + } + + /** + * Set the four components of this vector to the first four elements of the given array. + * + * @param xyzw + * the array containing at least four elements + * @return this + */ + public Vector4f set(float[] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is read, use {@link #set(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z, w order + * @return this + * @see #set(int, ByteBuffer) + */ + public Vector4f set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z, w order + * @return this + */ + public Vector4f set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is read, use {@link #set(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * values will be read in x, y, z, w order + * @return this + * @see #set(int, FloatBuffer) + */ + public Vector4f set(FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * values will be read in x, y, z, w order + * @return this + */ + public Vector4f set(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 4 float values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector4f setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..3] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..3] + */ + public Vector4f setComponent(int component, float value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + case 3: + w = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public FloatBuffer get(FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public FloatBuffer get(int index, FloatBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector4fc getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Subtract the supplied vector from this one. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector4f sub(Vector4fc v) { + this.x = this.x - v.x(); + this.y = this.y - v.y(); + this.z = this.z - v.z(); + this.w = this.w - v.w(); + return this; + } + + /** + * Subtract (x, y, z, w) from this. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param w + * the w component to subtract + * @return this + */ + public Vector4f sub(float x, float y, float z, float w) { + this.x = this.x - x; + this.y = this.y - y; + this.z = this.z - z; + this.w = this.w - w; + return this; + } + + public Vector4f sub(Vector4fc v, Vector4f dest) { + dest.x = this.x - v.x(); + dest.y = this.y - v.y(); + dest.z = this.z - v.z(); + dest.w = this.w - v.w(); + return dest; + } + + public Vector4f sub(float x, float y, float z, float w, Vector4f dest) { + dest.x = this.x - x; + dest.y = this.y - y; + dest.z = this.z - z; + dest.w = this.w - w; + return dest; + } + + /** + * Add the supplied vector to this one. + * + * @param v + * the vector to add + * @return this + */ + public Vector4f add(Vector4fc v) { + this.x = x + v.x(); + this.y = y + v.y(); + this.z = z + v.z(); + this.w = w + v.w(); + return this; + } + + public Vector4f add(Vector4fc v, Vector4f dest) { + dest.x = x + v.x(); + dest.y = y + v.y(); + dest.z = z + v.z(); + dest.w = w + v.w(); + return dest; + } + + /** + * Increment the components of this vector by the given values. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @param w + * the w component to add + * @return this + */ + public Vector4f add(float x, float y, float z, float w) { + this.x = this.x + x; + this.y = this.y + y; + this.z = this.z + z; + this.w = this.w + w; + return this; + } + + public Vector4f add(float x, float y, float z, float w, Vector4f dest) { + dest.x = this.x + x; + dest.y = this.y + y; + dest.z = this.z + z; + dest.w = this.w + w; + return dest; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector4f fma(Vector4fc a, Vector4fc b) { + this.x = Math.fma(a.x(), b.x(), x); + this.y = Math.fma(a.y(), b.y(), y); + this.z = Math.fma(a.z(), b.z(), z); + this.w = Math.fma(a.w(), b.w(), w); + return this; + } + + /** + * Add the component-wise multiplication of a * b to this vector. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @return this + */ + public Vector4f fma(float a, Vector4fc b) { + this.x = Math.fma(a, b.x(), x); + this.y = Math.fma(a, b.y(), y); + this.z = Math.fma(a, b.z(), z); + this.w = Math.fma(a, b.w(), w); + return this; + } + + public Vector4f fma(Vector4fc a, Vector4fc b, Vector4f dest) { + dest.x = Math.fma(a.x(), b.x(), x); + dest.y = Math.fma(a.y(), b.y(), y); + dest.z = Math.fma(a.z(), b.z(), z); + dest.w = Math.fma(a.w(), b.w(), w); + return dest; + } + + public Vector4f fma(float a, Vector4fc b, Vector4f dest) { + dest.x = Math.fma(a, b.x(), x); + dest.y = Math.fma(a, b.y(), y); + dest.z = Math.fma(a, b.z(), z); + dest.w = Math.fma(a, b.w(), w); + return dest; + } + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in this. + * + * @param a + * the multiplicand + * @param b + * the addend + * @return this + */ + public Vector4f mulAdd(Vector4fc a, Vector4fc b) { + this.x = Math.fma(x, a.x(), b.x()); + this.y = Math.fma(y, a.y(), b.y()); + this.z = Math.fma(z, a.z(), b.z()); + return this; + } + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in this. + * + * @param a + * the multiplicand + * @param b + * the addend + * @return this + */ + public Vector4f mulAdd(float a, Vector4fc b) { + this.x = Math.fma(x, a, b.x()); + this.y = Math.fma(y, a, b.y()); + this.z = Math.fma(z, a, b.z()); + return this; + } + + public Vector4f mulAdd(Vector4fc a, Vector4fc b, Vector4f dest) { + dest.x = Math.fma(x, a.x(), b.x()); + dest.y = Math.fma(y, a.y(), b.y()); + dest.z = Math.fma(z, a.z(), b.z()); + return dest; + } + + public Vector4f mulAdd(float a, Vector4fc b, Vector4f dest) { + dest.x = Math.fma(x, a, b.x()); + dest.y = Math.fma(y, a, b.y()); + dest.z = Math.fma(z, a, b.z()); + return dest; + } + + /** + * Multiply this Vector4f component-wise by another Vector4f. + * + * @param v + * the other vector + * @return this + */ + public Vector4f mul(Vector4fc v) { + this.x = x * v.x(); + this.y = y * v.y(); + this.z = z * v.z(); + this.w = w * v.w(); + return this; + } + + public Vector4f mul(Vector4fc v, Vector4f dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + dest.z = z * v.z(); + dest.w = w * v.w(); + return dest; + } + + /** + * Divide this Vector4f component-wise by another Vector4f. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector4f div(Vector4fc v) { + this.x = x / v.x(); + this.y = y / v.y(); + this.z = z / v.z(); + this.w = w / v.w(); + return this; + } + + public Vector4f div(Vector4fc v, Vector4f dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + dest.z = z / v.z(); + dest.w = w / v.w(); + return dest; + } + + /** + * Multiply the given matrix mat with this Vector4f and store the result in + * this. + * + * @param mat + * the matrix to multiply the vector with + * @return this + */ + public Vector4f mul(Matrix4fc mat) { + if ((mat.properties() & Matrix4fc.PROPERTY_AFFINE) != 0) + return mulAffine(mat, this); + return mulGeneric(mat, this); + } + public Vector4f mul(Matrix4fc mat, Vector4f dest) { + if ((mat.properties() & Matrix4fc.PROPERTY_AFFINE) != 0) + return mulAffine(mat, dest); + return mulGeneric(mat, dest); + } + + /** + * Multiply the transpose of the given matrix mat with this Vector4f and store the result in + * this. + * + * @param mat + * the matrix whose transpose to multiply the vector with + * @return this + */ + public Vector4f mulTranspose(Matrix4fc mat) { + if ((mat.properties() & Matrix4fc.PROPERTY_AFFINE) != 0) + return mulAffineTranspose(mat, this); + return mulGenericTranspose(mat, this); + } + public Vector4f mulTranspose(Matrix4fc mat, Vector4f dest) { + if ((mat.properties() & Matrix4fc.PROPERTY_AFFINE) != 0) + return mulAffineTranspose(mat, dest); + return mulGenericTranspose(mat, dest); + } + + public Vector4f mulAffine(Matrix4fc mat, Vector4f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + dest.w = w; + return dest; + } + + private Vector4f mulGeneric(Matrix4fc mat, Vector4f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + dest.w = Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + return dest; + } + + public Vector4f mulAffineTranspose(Matrix4fc mat, Vector4f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, mat.m02() * z)); + dest.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, mat.m12() * z)); + dest.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, mat.m22() * z)); + dest.w = Math.fma(mat.m30(), x, Math.fma(mat.m31(), y, mat.m32() * z + w)); + return dest; + } + private Vector4f mulGenericTranspose(Matrix4fc mat, Vector4f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m01(), y, Math.fma(mat.m02(), z, mat.m03() * w))); + dest.y = Math.fma(mat.m10(), x, Math.fma(mat.m11(), y, Math.fma(mat.m12(), z, mat.m13() * w))); + dest.z = Math.fma(mat.m20(), x, Math.fma(mat.m21(), y, Math.fma(mat.m22(), z, mat.m23() * w))); + dest.w = Math.fma(mat.m30(), x, Math.fma(mat.m31(), y, Math.fma(mat.m32(), z, mat.m33() * w))); + return dest; + } + + /** + * Multiply the given matrix mat with this Vector4f and store the result in + * this. + * + * @param mat + * the matrix to multiply the vector with + * @return this + */ + public Vector4f mul(Matrix4x3fc mat) { + float x = this.x, y = this.y, z = this.z, w = this.w; + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + this.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + this.w = w; + return this; + } + + public Vector4f mul(Matrix4x3fc mat, Vector4f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))); + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))); + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))); + dest.w = w; + return dest; + } + + public Vector4f mulProject(Matrix4fc mat, Vector4f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + float invW = 1.0f / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))) * invW; + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))) * invW; + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))) * invW; + dest.w = 1.0f; + return dest; + } + + /** + * Multiply the given matrix mat with this Vector4f, perform perspective division. + * + * @param mat + * the matrix to multiply this vector by + * @return this + */ + public Vector4f mulProject(Matrix4fc mat) { + float x = this.x, y = this.y, z = this.z, w = this.w; + float invW = 1.0f / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + this.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))) * invW; + this.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))) * invW; + this.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))) * invW; + this.w = 1.0f; + return this; + } + + public Vector3f mulProject(Matrix4fc mat, Vector3f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + float invW = 1.0f / Math.fma(mat.m03(), x, Math.fma(mat.m13(), y, Math.fma(mat.m23(), z, mat.m33() * w))); + dest.x = Math.fma(mat.m00(), x, Math.fma(mat.m10(), y, Math.fma(mat.m20(), z, mat.m30() * w))) * invW; + dest.y = Math.fma(mat.m01(), x, Math.fma(mat.m11(), y, Math.fma(mat.m21(), z, mat.m31() * w))) * invW; + dest.z = Math.fma(mat.m02(), x, Math.fma(mat.m12(), y, Math.fma(mat.m22(), z, mat.m32() * w))) * invW; + return dest; + } + + /** + * Multiply all components of this {@link Vector4f} by the given scalar + * value. + * + * @param scalar + * the scalar to multiply by + * @return this + */ + public Vector4f mul(float scalar) { + this.x = x * scalar; + this.y = y * scalar; + this.z = z * scalar; + this.w = w * scalar; + return this; + } + + public Vector4f mul(float scalar, Vector4f dest) { + dest.x = x * scalar; + dest.y = y * scalar; + dest.z = z * scalar; + dest.w = w * scalar; + return dest; + } + + /** + * Multiply the components of this Vector4f by the given scalar values and store the result in this. + * + * @param x + * the x component to multiply by + * @param y + * the y component to multiply by + * @param z + * the z component to multiply by + * @param w + * the w component to multiply by + * @return this + */ + public Vector4f mul(float x, float y, float z, float w) { + this.x = this.x * x; + this.y = this.y * y; + this.z = this.z * z; + this.w = this.w * w; + return this; + } + + public Vector4f mul(float x, float y, float z, float w, Vector4f dest) { + dest.x = this.x * x; + dest.y = this.y * y; + dest.z = this.z * z; + dest.w = this.w * w; + return dest; + } + + /** + * Divide all components of this {@link Vector4f} by the given scalar + * value. + * + * @param scalar + * the scalar to divide by + * @return this + */ + public Vector4f div(float scalar) { + float inv = 1.0f / scalar; + this.x = x * inv; + this.y = y * inv; + this.z = z * inv; + this.w = w * inv; + return this; + } + + public Vector4f div(float scalar, Vector4f dest) { + float inv = 1.0f / scalar; + dest.x = x * inv; + dest.y = y * inv; + dest.z = z * inv; + dest.w = w * inv; + return dest; + } + + /** + * Divide the components of this Vector4f by the given scalar values and store the result in this. + * + * @param x + * the x component to divide by + * @param y + * the y component to divide by + * @param z + * the z component to divide by + * @param w + * the w component to divide by + * @return this + */ + public Vector4f div(float x, float y, float z, float w) { + this.x = this.x / x; + this.y = this.y / y; + this.z = this.z / z; + this.w = this.w / w; + return this; + } + + public Vector4f div(float x, float y, float z, float w, Vector4f dest) { + dest.x = this.x / x; + dest.y = this.y / y; + dest.z = this.z / z; + dest.w = this.w / w; + return dest; + } + + /** + * Rotate this vector by the given quaternion quat and store the result in this. + * + * @see Quaternionf#transform(Vector4f) + * + * @param quat + * the quaternion to rotate this vector + * @return this + */ + public Vector4f rotate(Quaternionfc quat) { + return quat.transform(this, this); + } + + public Vector4f rotate(Quaternionfc quat, Vector4f dest) { + return quat.transform(this, dest); + } + + /** + * Rotate this vector the specified radians around the given rotation axis. + * + * @param angle + * the angle in radians + * @param x + * the x component of the rotation axis + * @param y + * the y component of the rotation axis + * @param z + * the z component of the rotation axis + * @return this + */ + public Vector4f rotateAbout(float angle, float x, float y, float z) { + if (y == 0.0f && z == 0.0f && Math.absEqualsOne(x)) + return rotateX(x * angle, this); + else if (x == 0.0f && z == 0.0f && Math.absEqualsOne(y)) + return rotateY(y * angle, this); + else if (x == 0.0f && y == 0.0f && Math.absEqualsOne(z)) + return rotateZ(z * angle, this); + return rotateAxisInternal(angle, x, y, z, this); + } + + public Vector4f rotateAxis(float angle, float aX, float aY, float aZ, Vector4f dest) { + if (aY == 0.0f && aZ == 0.0f && Math.absEqualsOne(aX)) + return rotateX(aX * angle, dest); + else if (aX == 0.0f && aZ == 0.0f && Math.absEqualsOne(aY)) + return rotateY(aY * angle, dest); + else if (aX == 0.0f && aY == 0.0f && Math.absEqualsOne(aZ)) + return rotateZ(aZ * angle, dest); + return rotateAxisInternal(angle, aX, aY, aZ, dest); + } + private Vector4f rotateAxisInternal(float angle, float aX, float aY, float aZ, Vector4f dest) { + float hangle = angle * 0.5f; + float sinAngle = Math.sin(hangle); + float qx = aX * sinAngle, qy = aY * sinAngle, qz = aZ * sinAngle; + float qw = Math.cosFromSin(sinAngle, hangle); + float w2 = qw * qw, x2 = qx * qx, y2 = qy * qy, z2 = qz * qz, zw = qz * qw; + float xy = qx * qy, xz = qx * qz, yw = qy * qw, yz = qy * qz, xw = qx * qw; + float x = this.x, y = this.y, z = this.z; + dest.x = (w2 + x2 - z2 - y2) * x + (-zw + xy - zw + xy) * y + (yw + xz + xz + yw) * z; + dest.y = (xy + zw + zw + xy) * x + ( y2 - z2 + w2 - x2) * y + (yz + yz - xw - xw) * z; + dest.z = (xz - yw + xz - yw) * x + ( yz + yz + xw + xw) * y + (z2 - y2 - x2 + w2) * z; + return dest; + } + + /** + * Rotate this vector the specified radians around the X axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector4f rotateX(float angle) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float y = this.y * cos - this.z * sin; + float z = this.y * sin + this.z * cos; + this.y = y; + this.z = z; + return this; + } + + public Vector4f rotateX(float angle, Vector4f dest) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float y = this.y * cos - this.z * sin; + float z = this.y * sin + this.z * cos; + dest.x = this.x; + dest.y = y; + dest.z = z; + dest.w = this.w; + return dest; + } + + /** + * Rotate this vector the specified radians around the Y axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector4f rotateY(float angle) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float x = this.x * cos + this.z * sin; + float z = -this.x * sin + this.z * cos; + this.x = x; + this.z = z; + return this; + } + + public Vector4f rotateY(float angle, Vector4f dest) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float x = this.x * cos + this.z * sin; + float z = -this.x * sin + this.z * cos; + dest.x = x; + dest.y = this.y; + dest.z = z; + dest.w = this.w; + return dest; + } + + /** + * Rotate this vector the specified radians around the Z axis. + * + * @param angle + * the angle in radians + * @return this + */ + public Vector4f rotateZ(float angle) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float x = this.x * cos - this.y * sin; + float y = this.x * sin + this.y * cos; + this.x = x; + this.y = y; + return this; + } + + public Vector4f rotateZ(float angle, Vector4f dest) { + float sin = Math.sin(angle), cos = Math.cosFromSin(sin, angle); + float x = this.x * cos - this.y * sin; + float y = this.x * sin + this.y * cos; + dest.x = x; + dest.y = y; + dest.z = this.z; + dest.w = this.w; + return dest; + } + + public float lengthSquared() { + return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + } + + /** + * Get the length squared of a 4-dimensional single-precision vector. + * + * @param x the vector's x component + * @param y the vector's y component + * @param z the vector's z component + * @param w the vector's w component + * + * @return the length squared of the given vector + * + * @author F. Neurath + */ + public static float lengthSquared(float x, float y, float z, float w) { + return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + } + + /** + * Get the length squared of a 4-dimensional int vector. + * + * @param x the vector's x component + * @param y the vector's y component + * @param z the vector's z component + * @param w the vector's w component + * + * @return the length squared of the given vector + */ + public static float lengthSquared(int x, int y, int z, int w) { + return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + } + + public float length() { + return Math.sqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + } + + /** + * Get the length of a 4-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * @param w The vector's w component + * + * @return the length of the given vector + * + * @author F. Neurath + */ + public static float length(float x, float y, float z, float w) { + return Math.sqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)))); + } + + /** + * Normalizes this vector. + * + * @return this + */ + public Vector4f normalize() { + float invLength = 1.0f / length(); + this.x = x * invLength; + this.y = y * invLength; + this.z = z * invLength; + this.w = w * invLength; + return this; + } + + public Vector4f normalize(Vector4f dest) { + float invLength = 1.0f / length(); + dest.x = x * invLength; + dest.y = y * invLength; + dest.z = z * invLength; + dest.w = w * invLength; + return dest; + } + + /** + * Scale this vector to have the given length. + * + * @param length + * the desired length + * @return this + */ + public Vector4f normalize(float length) { + float invLength = 1.0f / length() * length; + this.x = x * invLength; + this.y = y * invLength; + this.z = z * invLength; + this.w = w * invLength; + return this; + } + + public Vector4f normalize(float length, Vector4f dest) { + float invLength = 1.0f / length() * length; + dest.x = x * invLength; + dest.y = y * invLength; + dest.z = z * invLength; + dest.w = w * invLength; + return dest; + } + + /** + * Normalize this vector by computing only the norm of (x, y, z). + * + * @return this + */ + public Vector4f normalize3() { + float invLength = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + this.x = x * invLength; + this.y = y * invLength; + this.z = z * invLength; + this.w = w * invLength; + return this; + } + + public Vector4f normalize3(Vector4f dest) { + float invLength = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); + dest.x = x * invLength; + dest.y = y * invLength; + dest.z = z * invLength; + dest.w = w * invLength; + return dest; + } + + public float distance(Vector4fc v) { + float dx = this.x - v.x(); + float dy = this.y - v.y(); + float dz = this.z - v.z(); + float dw = this.w - v.w(); + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw)))); + } + + public float distance(float x, float y, float z, float w) { + float dx = this.x - x; + float dy = this.y - y; + float dz = this.z - z; + float dw = this.w - w; + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw)))); + } + + public float distanceSquared(Vector4fc v) { + float dx = this.x - v.x(); + float dy = this.y - v.y(); + float dz = this.z - v.z(); + float dw = this.w - v.w(); + return Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw))); + } + + public float distanceSquared(float x, float y, float z, float w) { + float dx = this.x - x; + float dy = this.y - y; + float dz = this.z - z; + float dw = this.w - w; + return Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw))); + } + + /** + * Return the distance between (x1, y1, z1, w1) and (x2, y2, z2, w2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param w1 + * the w component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @param w2 + * the 2 component of the second vector + * @return the euclidean distance + */ + public static float distance(float x1, float y1, float z1, float w1, float x2, float y2, float z2, float w2) { + float dx = x1 - x2; + float dy = y1 - y2; + float dz = z1 - z2; + float dw = w1 - w2; + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw)))); + } + + /** + * Return the squared distance between (x1, y1, z1, w1) and (x2, y2, z2, w2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param w1 + * the w component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @param w2 + * the w component of the second vector + * @return the euclidean distance squared + */ + public static float distanceSquared(float x1, float y1, float z1, float w1, float x2, float y2, float z2, float w2) { + float dx = x1 - x2; + float dy = y1 - y2; + float dz = z1 - z2; + float dw = w1 - w2; + return Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw))); + } + + public float dot(Vector4fc v) { + return Math.fma(this.x, v.x(), Math.fma(this.y, v.y(), Math.fma(this.z, v.z(), this.w * v.w()))); + } + + public float dot(float x, float y, float z, float w) { + return Math.fma(this.x, x, Math.fma(this.y, y, Math.fma(this.z, z, this.w * w))); + } + + public float angleCos(Vector4fc v) { + float x = this.x, y = this.y, z = this.z, w = this.w; + float length1Squared = Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); + float length2Squared = Math.fma(v.x(), v.x(), Math.fma(v.y(), v.y(), Math.fma(v.z(), v.z(), v.w() * v.w()))); + float dot = Math.fma(x, v.x(), Math.fma(y, v.y(), Math.fma(z, v.z(), w * v.w()))); + return dot / Math.sqrt(length1Squared * length2Squared); + } + + public float angle(Vector4fc v) { + float cos = angleCos(v); + // This is because sometimes cos goes above 1 or below -1 because of lost precision + cos = cos < 1 ? cos : 1; + cos = cos > -1 ? cos : -1; + return Math.acos(cos); + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector4f zero() { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + return this; + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector4f negate() { + this.x = -x; + this.y = -y; + this.z = -z; + this.w = -w; + return this; + } + + public Vector4f negate(Vector4f dest) { + dest.x = -x; + dest.y = -y; + dest.z = -z; + dest.w = -w; + return dest; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + Runtime.format(x, formatter) + " " + Runtime.format(y, formatter) + " " + Runtime.format(z, formatter) + " " + Runtime.format(w, formatter) + ")"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(x); + out.writeFloat(y); + out.writeFloat(z); + out.writeFloat(w); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + this.set(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector4f min(Vector4fc v) { + float x = this.x, y = this.y, z = this.z, w = this.w; + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + this.z = z < v.z() ? z : v.z(); + this.w = w < v.w() ? w : v.w(); + return this; + } + + public Vector4f min(Vector4fc v, Vector4f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + dest.z = z < v.z() ? z : v.z(); + dest.w = w < v.w() ? w : v.w(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector4f max(Vector4fc v) { + float x = this.x, y = this.y, z = this.z, w = this.w; + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + this.z = z > v.z() ? z : v.z(); + this.w = w > v.w() ? w : v.w(); + return this; + } + + public Vector4f max(Vector4fc v, Vector4f dest) { + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + dest.z = z > v.z() ? z : v.z(); + dest.w = w > v.w() ? w : v.w(); + return dest; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(w); + result = prime * result + Float.floatToIntBits(x); + result = prime * result + Float.floatToIntBits(y); + result = prime * result + Float.floatToIntBits(z); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector4f other = (Vector4f) obj; + if (Float.floatToIntBits(w) != Float.floatToIntBits(other.w)) + return false; + if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) + return false; + if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) + return false; + if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z)) + return false; + return true; + } + + public boolean equals(Vector4fc v, float delta) { + if (this == v) + return true; + if (v == null) + return false; + if (!(v instanceof Vector4fc)) + return false; + if (!Runtime.equals(x, v.x(), delta)) + return false; + if (!Runtime.equals(y, v.y(), delta)) + return false; + if (!Runtime.equals(z, v.z(), delta)) + return false; + if (!Runtime.equals(w, v.w(), delta)) + return false; + return true; + } + + public boolean equals(float x, float y, float z, float w) { + if (Float.floatToIntBits(this.x) != Float.floatToIntBits(x)) + return false; + if (Float.floatToIntBits(this.y) != Float.floatToIntBits(y)) + return false; + if (Float.floatToIntBits(this.z) != Float.floatToIntBits(z)) + return false; + if (Float.floatToIntBits(this.w) != Float.floatToIntBits(w)) + return false; + return true; + } + + public Vector4f smoothStep(Vector4fc v, float t, Vector4f dest) { + float t2 = t * t; + float t3 = t2 * t; + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = (x + x - v.x() - v.x()) * t3 + (3.0f * v.x() - 3.0f * x) * t2 + x * t + x; + dest.y = (y + y - v.y() - v.y()) * t3 + (3.0f * v.y() - 3.0f * y) * t2 + y * t + y; + dest.z = (z + z - v.z() - v.z()) * t3 + (3.0f * v.z() - 3.0f * z) * t2 + z * t + z; + dest.w = (w + w - v.w() - v.w()) * t3 + (3.0f * v.w() - 3.0f * w) * t2 + w * t + w; + return dest; + } + + public Vector4f hermite(Vector4fc t0, Vector4fc v1, Vector4fc t1, float t, Vector4f dest) { + float t2 = t * t; + float t3 = t2 * t; + float x = this.x, y = this.y, z = this.z, w = this.w; + dest.x = (x + x - v1.x() - v1.x() + t1.x() + t0.x()) * t3 + (3.0f * v1.x() - 3.0f * x - t0.x() - t0.x() - t1.x()) * t2 + x * t + x; + dest.y = (y + y - v1.y() - v1.y() + t1.y() + t0.y()) * t3 + (3.0f * v1.y() - 3.0f * y - t0.y() - t0.y() - t1.y()) * t2 + y * t + y; + dest.z = (z + z - v1.z() - v1.z() + t1.z() + t0.z()) * t3 + (3.0f * v1.z() - 3.0f * z - t0.z() - t0.z() - t1.z()) * t2 + z * t + z; + dest.w = (w + w - v1.w() - v1.w() + t1.w() + t0.w()) * t3 + (3.0f * v1.w() - 3.0f * w - t0.w() - t0.w() - t1.w()) * t2 + w * t + w; + return dest; + } + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in this. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @return this + */ + public Vector4f lerp(Vector4fc other, float t) { + this.x = Math.fma(other.x() - x, t, x); + this.y = Math.fma(other.y() - y, t, y); + this.z = Math.fma(other.z() - z, t, z); + this.w = Math.fma(other.w() - w, t, w); + return this; + } + + public Vector4f lerp(Vector4fc other, float t, Vector4f dest) { + dest.x = Math.fma(other.x() - x, t, x); + dest.y = Math.fma(other.y() - y, t, y); + dest.z = Math.fma(other.z() - z, t, z); + dest.w = Math.fma(other.w() - w, t, w); + return dest; + } + + public float get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IllegalArgumentException(); + } + } + + public Vector4i get(int mode, Vector4i dest) { + dest.x = Math.roundUsing(this.x(), mode); + dest.y = Math.roundUsing(this.y(), mode); + dest.z = Math.roundUsing(this.z(), mode); + dest.w = Math.roundUsing(this.w(), mode); + return dest; + } + + public Vector4f get(Vector4f dest) { + dest.x = this.x(); + dest.y = this.y(); + dest.z = this.z(); + dest.w = this.w(); + return dest; + } + + public Vector4d get(Vector4d dest) { + dest.x = this.x(); + dest.y = this.y(); + dest.z = this.z(); + dest.w = this.w(); + return dest; + } + + public int maxComponent() { + float absX = Math.abs(x); + float absY = Math.abs(y); + float absZ = Math.abs(z); + float absW = Math.abs(w); + if (absX >= absY && absX >= absZ && absX >= absW) { + return 0; + } else if (absY >= absZ && absY >= absW) { + return 1; + } else if (absZ >= absW) { + return 2; + } + return 3; + } + + public int minComponent() { + float absX = Math.abs(x); + float absY = Math.abs(y); + float absZ = Math.abs(z); + float absW = Math.abs(w); + if (absX < absY && absX < absZ && absX < absW) { + return 0; + } else if (absY < absZ && absY < absW) { + return 1; + } else if (absZ < absW) { + return 2; + } + return 3; + } + + /** + * Set each component of this vector to the largest (closest to positive + * infinity) {@code float} value that is less than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector4f floor() { + this.x = Math.floor(x); + this.y = Math.floor(y); + this.z = Math.floor(z); + this.w = Math.floor(w); + return this; + } + + public Vector4f floor(Vector4f dest) { + dest.x = Math.floor(x); + dest.y = Math.floor(y); + dest.z = Math.floor(z); + dest.w = Math.floor(w); + return dest; + } + + /** + * Set each component of this vector to the smallest (closest to negative + * infinity) {@code float} value that is greater than or equal to that + * component and is equal to a mathematical integer. + * + * @return this + */ + public Vector4f ceil() { + this.x = Math.ceil(x); + this.y = Math.ceil(y); + this.z = Math.ceil(z); + this.w = Math.ceil(w); + return this; + } + + public Vector4f ceil(Vector4f dest) { + dest.x = Math.ceil(x); + dest.y = Math.ceil(y); + dest.z = Math.ceil(z); + dest.w = Math.ceil(w); + return dest; + } + + /** + * Set each component of this vector to the closest float that is equal to + * a mathematical integer, with ties rounding to positive infinity. + * + * @return this + */ + public Vector4f round() { + this.x = Math.round(x); + this.y = Math.round(y); + this.z = Math.round(z); + this.w = Math.round(w); + return this; + } + + public Vector4f round(Vector4f dest) { + dest.x = Math.round(x); + dest.y = Math.round(y); + dest.z = Math.round(z); + dest.w = Math.round(w); + return dest; + } + + public boolean isFinite() { + return Math.isFinite(x) && Math.isFinite(y) && Math.isFinite(z) && Math.isFinite(w); + } + + /** + * Compute the absolute of each of this vector's components. + * + * @return this + */ + public Vector4f absolute() { + this.x = Math.abs(x); + this.y = Math.abs(y); + this.z = Math.abs(z); + this.w = Math.abs(w); + return this; + } + + public Vector4f absolute(Vector4f dest) { + dest.x = Math.abs(x); + dest.y = Math.abs(y); + dest.z = Math.abs(z); + dest.w = Math.abs(w); + return dest; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4fc.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4fc.java new file mode 100644 index 000000000..31a8e9507 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4fc.java @@ -0,0 +1,838 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * Interface to a read-only view of a 4-dimensional vector of single-precision floats. + * + * @author Kai Burjack + */ +public interface Vector4fc { + + /** + * @return the value of the x component + */ + float x(); + + /** + * @return the value of the y component + */ + float y(); + + /** + * @return the value of the z component + */ + float z(); + + /** + * @return the value of the w component + */ + float w(); + + /** + * Store this vector into the supplied {@link FloatBuffer} at the current + * buffer {@link FloatBuffer#position() position}. + *

+ * This method will not increment the position of the given FloatBuffer. + *

+ * In order to specify the offset into the FloatBuffer at which + * the vector is stored, use {@link #get(int, FloatBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + * @see #get(int, FloatBuffer) + */ + FloatBuffer get(FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link FloatBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given FloatBuffer. + * + * @param index + * the absolute position into the FloatBuffer + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + FloatBuffer get(int index, FloatBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which + * the vector is stored, use {@link #get(int, ByteBuffer)}, taking + * the absolute position as parameter. + * + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + * @see #get(int, ByteBuffer) + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the specified + * absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector4fc getToAddress(long address); + + /** + * Subtract the supplied vector from this one and store the result in dest. + * + * @param v + * the vector to subtract from this + * @param dest + * will hold the result + * @return dest + */ + Vector4f sub(Vector4fc v, Vector4f dest); + + /** + * Subtract (x, y, z, w) from this and store the result in dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param w + * the w component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector4f sub(float x, float y, float z, float w, Vector4f dest); + + /** + * Add the supplied vector to this one and store the result in dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector4f add(Vector4fc v, Vector4f dest); + + /** + * Increment the components of this vector by the given values and store the result in dest. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @param w + * the w component to add + * @param dest + * will hold the result + * @return dest + */ + Vector4f add(float x, float y, float z, float w, Vector4f dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector4f fma(Vector4fc a, Vector4fc b, Vector4f dest); + + /** + * Add the component-wise multiplication of a * b to this vector + * and store the result in dest. + * + * @param a + * the first multiplicand + * @param b + * the second multiplicand + * @param dest + * will hold the result + * @return dest + */ + Vector4f fma(float a, Vector4fc b, Vector4f dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector4f mulAdd(Vector4fc a, Vector4fc b, Vector4f dest); + + /** + * Add the component-wise multiplication of this * a to b + * and store the result in dest. + * + * @param a + * the multiplicand + * @param b + * the addend + * @param dest + * will hold the result + * @return dest + */ + Vector4f mulAdd(float a, Vector4fc b, Vector4f dest); + + /** + * Multiply this Vector4f component-wise by another Vector4f and store the result in dest. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector4f mul(Vector4fc v, Vector4f dest); + + /** + * Divide this Vector4f component-wise by another Vector4f and store the result in dest. + * + * @param v + * the vector to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector4f div(Vector4fc v, Vector4f dest); + + /** + * Multiply the given matrix mat with this Vector4f and store the result in + * dest. + * + * @param mat + * the matrix to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4f mul(Matrix4fc mat, Vector4f dest); + + /** + * Multiply the transpose of the given matrix mat with this Vector4f and store the result in + * dest. + * + * @param mat + * the matrix whose transpose to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4f mulTranspose(Matrix4fc mat, Vector4f dest); + + /** + * Multiply the given affine matrix mat with this Vector4f and store the result in + * dest. + * + * @param mat + * the affine matrix to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4f mulAffine(Matrix4fc mat, Vector4f dest); + + /** + * Multiply the transpose of the given affine matrix mat with this Vector4f and store the result in + * dest. + * + * @param mat + * the affine matrix whose transpose to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4f mulAffineTranspose(Matrix4fc mat, Vector4f dest); + + /** + * Multiply the given matrix mat with this Vector4f and store the result in + * dest. + * + * @param mat + * the matrix to multiply the vector with + * @param dest + * the destination vector to hold the result + * @return dest + */ + Vector4f mul(Matrix4x3fc mat, Vector4f dest); + + /** + * Multiply the given matrix mat with this Vector4f, perform perspective division + * and store the result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector4f mulProject(Matrix4fc mat, Vector4f dest); + + /** + * Multiply the given matrix mat with this Vector4f, perform perspective division + * and store the (x, y, z) result in dest. + * + * @param mat + * the matrix to multiply this vector by + * @param dest + * will hold the result + * @return dest + */ + Vector3f mulProject(Matrix4fc mat, Vector3f dest); + + /** + * Multiply all components of this {@link Vector4f} by the given scalar + * value and store the result in dest. + * + * @param scalar + * the scalar to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector4f mul(float scalar, Vector4f dest); + + /** + * Multiply the components of this Vector4f by the given scalar values and store the result in dest. + * + * @param x + * the x component to multiply by + * @param y + * the y component to multiply by + * @param z + * the z component to multiply by + * @param w + * the w component to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector4f mul(float x, float y, float z, float w, Vector4f dest); + + /** + * Divide all components of this {@link Vector4f} by the given scalar + * value and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector4f div(float scalar, Vector4f dest); + + /** + * Divide the components of this Vector4f by the given scalar values and store the result in dest. + * + * @param x + * the x component to divide by + * @param y + * the y component to divide by + * @param z + * the z component to divide by + * @param w + * the w component to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector4f div(float x, float y, float z, float w, Vector4f dest); + + /** + * Rotate this vector by the given quaternion quat and store the result in dest. + * + * @see Quaternionf#transform(Vector4f) + * + * @param quat + * the quaternion to rotate this vector + * @param dest + * will hold the result + * @return dest + */ + Vector4f rotate(Quaternionfc quat, Vector4f dest); + + /** + * Rotate this vector the specified radians around the given rotation axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param aX + * the x component of the rotation axis + * @param aY + * the y component of the rotation axis + * @param aZ + * the z component of the rotation axis + * @param dest + * will hold the result + * @return dest + */ + Vector4f rotateAxis(float angle, float aX, float aY, float aZ, Vector4f dest); + + /** + * Rotate this vector the specified radians around the X axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector4f rotateX(float angle, Vector4f dest); + + /** + * Rotate this vector the specified radians around the Y axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector4f rotateY(float angle, Vector4f dest); + + /** + * Rotate this vector the specified radians around the Z axis and store the result + * into dest. + * + * @param angle + * the angle in radians + * @param dest + * will hold the result + * @return dest + */ + Vector4f rotateZ(float angle, Vector4f dest); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + float lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + float length(); + + /** + * Normalizes this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f normalize(Vector4f dest); + + /** + * Scale this vector to have the given length and store the result in dest. + * + * @param length + * the desired length + * @param dest + * will hold the result + * @return dest + */ + Vector4f normalize(float length, Vector4f dest); + + /** + * Normalize this vector by computing only the norm of (x, y, z) and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f normalize3(Vector4f dest); + + /** + * Return the distance between this Vector and v. + * + * @param v + * the other vector + * @return the distance + */ + float distance(Vector4fc v); + + /** + * Return the distance between this vector and (x, y, z, w). + * + * @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 euclidean distance + */ + float distance(float x, float y, float z, float w); + + /** + * Return the square of the distance between this vector and v. + * + * @param v + * the other vector + * @return the squared of the distance + */ + float distanceSquared(Vector4fc v); + + /** + * Return the square of the distance between this vector and + * (x, y, z, w). + * + * @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 square of the distance + */ + float distanceSquared(float x, float y, float z, float w); + + /** + * Compute the dot product (inner product) of this vector and v + * . + * + * @param v + * the other vector + * @return the dot product + */ + float dot(Vector4fc v); + + /** + * Compute the dot product (inner product) of this vector and (x, y, z, w). + * + * @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 + */ + float dot(float x, float y, float z, float w); + + /** + * Return the cosine of the angle between this vector and the supplied vector. Use this instead of Math.cos(angle(v)). + * + * @see #angle(Vector4fc) + * + * @param v + * the other vector + * @return the cosine of the angle + */ + float angleCos(Vector4fc v); + + /** + * Return the angle between this vector and the supplied vector. + * + * @see #angleCos(Vector4fc) + * + * @param v + * the other vector + * @return the angle, in radians + */ + float angle(Vector4fc v); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f negate(Vector4f dest); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector4f min(Vector4fc v, Vector4f dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector4f max(Vector4fc v, Vector4f dest); + + /** + * Linearly interpolate this and other using the given interpolation factor t + * and store the result in dest. + *

+ * If t is 0.0 then the result is this. If the interpolation factor is 1.0 + * then the result is other. + * + * @param other + * the other vector + * @param t + * the interpolation factor between 0.0 and 1.0 + * @param dest + * will hold the result + * @return dest + */ + Vector4f lerp(Vector4fc other, float t, Vector4f dest); + + /** + * Compute a smooth-step (i.e. hermite with zero tangents) interpolation + * between this vector and the given vector v and + * store the result in dest. + * + * @param v + * the other vector + * @param t + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Vector4f smoothStep(Vector4fc v, float t, Vector4f dest); + + /** + * Compute a hermite interpolation between this vector and its + * associated tangent t0 and the given vector v + * with its tangent t1 and store the result in + * dest. + * + * @param t0 + * the tangent of this vector + * @param v1 + * the other vector + * @param t1 + * the tangent of the other vector + * @param t + * the interpolation factor, within [0..1] + * @param dest + * will hold the result + * @return dest + */ + Vector4f hermite(Vector4fc t0, Vector4fc v1, Vector4fc t1, float t, Vector4f dest); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..3] + * @return the value + * @throws IllegalArgumentException if component is not within [0..3] + */ + float get(int component) throws IllegalArgumentException; + + /** + * Set the components of the given vector dest to those of this vector + * using the given {@link RoundingMode}. + * + * @param mode + * the {@link RoundingMode} to use + * @param dest + * will hold the result + * @return dest + */ + Vector4i get(int mode, Vector4i dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f get(Vector4f dest); + + /** + * Set the components of the given vector dest to those of this vector. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4d get(Vector4d dest); + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..3] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..3] + */ + int minComponent(); + + /** + * Compute for each component of this vector the largest (closest to positive + * infinity) {@code float} value that is less than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f floor(Vector4f dest); + + /** + * Compute for each component of this vector the smallest (closest to negative + * infinity) {@code float} value that is greater than or equal to that + * component and is equal to a mathematical integer and store the result in + * dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f ceil(Vector4f dest); + + /** + * Compute for each component of this vector the closest float that is equal to + * a mathematical integer, with ties rounding to positive infinity and store + * the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f round(Vector4f dest); + + /** + * Determine whether all components are finite floating-point values, that + * is, they are not {@link Float#isNaN() NaN} and not + * {@link Float#isInfinite() infinity}. + * + * @return {@code true} if all components are finite floating-point values; + * {@code false} otherwise + */ + boolean isFinite(); + + /** + * Compute the absolute of each of this vector's components + * and store the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4f absolute(Vector4f dest); + + /** + * Compare the vector components of this vector with the given vector using the given delta + * and return whether all of them are equal within a maximum difference of delta. + *

+ * Please note that this method is not used by any data structure such as {@link ArrayList} {@link HashSet} or {@link HashMap} + * and their operations, such as {@link ArrayList#contains(Object)} or {@link HashSet#remove(Object)}, since those + * data structures only use the {@link Object#equals(Object)} and {@link Object#hashCode()} methods. + * + * @param v + * the other vector + * @param delta + * the allowed maximum difference + * @return true whether all of the vector components are equal; false otherwise + */ + boolean equals(Vector4fc v, float delta); + + /** + * Compare the vector components of this vector with the given (x, y, z, w) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @param z + * the z component to compare to + * @param w + * the w component to compare to + * @return true if all the vector components are equal + */ + boolean equals(float x, float y, float z, float w); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4i.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4i.java new file mode 100644 index 000000000..8749284b9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4i.java @@ -0,0 +1,1212 @@ +/* + * The MIT License + * + * Copyright (c) 2015-2021 Richard Greenlees + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Contains the definition of a Vector comprising 4 ints and associated + * transformations. + * + * @author Richard Greenlees + * @author Kai Burjack + * @author Hans Uhlig + */ +public class Vector4i implements Externalizable, Cloneable, Vector4ic { + + private static final long serialVersionUID = 1L; + + /** + * The x component of the vector. + */ + public int x; + /** + * The y component of the vector. + */ + public int y; + /** + * The z component of the vector. + */ + public int z; + /** + * The w component of the vector. + */ + public int w; + + /** + * Create a new {@link Vector4i} of (0, 0, 0, 1). + */ + public Vector4i() { + this.w = 1; + } + + /** + * Create a new {@link Vector4i} with the same values as v. + * + * @param v + * the {@link Vector4ic} to copy the values from + */ + public Vector4i(Vector4ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + } + + /** + * Create a new {@link Vector4i} with the first three components from the + * given v and the given w. + * + * @param v + * the {@link Vector3ic} + * @param w + * the w component + */ + public Vector4i(Vector3ic v, int w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + } + + /** + * Create a new {@link Vector4i} with the first two components from the + * given v and the given z, and w. + * + * @param v + * the {@link Vector2ic} + * @param z + * the z component + * @param w + * the w component + */ + public Vector4i(Vector2ic v, int z, int w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4i} with the first three components from the + * given v and the given w and round using the given {@link RoundingMode}. + * + * @param v + * the {@link Vector3fc} to copy the values from + * @param w + * the w component + * @param mode + * the {@link RoundingMode} to use + */ + public Vector4i(Vector3fc v, float w, int mode) { + x = Math.roundUsing(v.x(), mode); + y = Math.roundUsing(v.y(), mode); + z = Math.roundUsing(v.z(), mode); + w = Math.roundUsing(w, mode); + } + + /** + * Create a new {@link Vector4i} and initialize its components to the rounded value of + * the given vector. + * + * @param v + * the {@link Vector4fc} to round and copy the values from + * @param mode + * the {@link RoundingMode} to use + */ + public Vector4i(Vector4fc v, int mode) { + x = Math.roundUsing(v.x(), mode); + y = Math.roundUsing(v.y(), mode); + z = Math.roundUsing(v.z(), mode); + w = Math.roundUsing(v.w(), mode); + } + + /** + * Create a new {@link Vector4i} and initialize its components to the rounded value of + * the given vector. + * + * @param v + * the {@link Vector4dc} to round and copy the values from + * @param mode + * the {@link RoundingMode} to use + */ + public Vector4i(Vector4dc v, int mode) { + x = Math.roundUsing(v.x(), mode); + y = Math.roundUsing(v.y(), mode); + z = Math.roundUsing(v.z(), mode); + w = Math.roundUsing(v.w(), mode); + } + + /** + * Create a new {@link Vector4i} and initialize all four components with the + * given value. + * + * @param s + * scalar value of all four components + */ + public Vector4i(int s) { + this.x = s; + this.y = s; + this.z = s; + this.w = s; + } + + /** + * Create a new {@link Vector4i} with the given component values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @param w + * the w component + */ + public Vector4i(int x, int y, int z, int w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Create a new {@link Vector4i} and initialize its four components from the first + * four elements of the given array. + * + * @param xyzw + * the array containing at least four elements + */ + public Vector4i(int[] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + } + + /** + * Create a new {@link Vector4i} and read this vector from the supplied + * {@link ByteBuffer} at the current buffer + * {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * read, use {@link #Vector4i(int, ByteBuffer)}, taking the absolute + * position as parameter. + * + * @see #Vector4i(int, ByteBuffer) + * + * @param buffer + * values will be read in x, y, z, w order + */ + public Vector4i(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector4i} and read this vector from the supplied + * {@link ByteBuffer} starting at the specified absolute buffer + * position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z, w order + */ + public Vector4i(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + /** + * Create a new {@link Vector4i} and read this vector from the supplied + * {@link IntBuffer} at the current buffer + * {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * read, use {@link #Vector4i(int, IntBuffer)}, taking the absolute position + * as parameter. + * + * @see #Vector4i(int, IntBuffer) + * + * @param buffer + * values will be read in x, y, z, w order + */ + public Vector4i(IntBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + } + + /** + * Create a new {@link Vector4i} and read this vector from the supplied + * {@link IntBuffer} starting at the specified absolute buffer + * position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param buffer + * values will be read in x, y, z, w order + */ + public Vector4i(int index, IntBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + } + + public int x() { + return this.x; + } + + public int y() { + return this.y; + } + + public int z() { + return this.z; + } + + public int w() { + return this.w; + } + + /** + * Set this {@link Vector4i} to the values of the given v. + * + * @param v + * the vector whose values will be copied into this + * @return this + */ + public Vector4i set(Vector4ic v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = v.w(); + return this; + } + + /** + * Set this {@link Vector4i} to the values of v using {@link RoundingMode#TRUNCATE} rounding. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @return this + */ + public Vector4i set(Vector4dc v) { + this.x = (int) v.x(); + this.y = (int) v.y(); + this.z = (int) v.z(); + this.w = (int) v.w(); + return this; + } + + /** + * Set this {@link Vector4i} to the values of v using the given {@link RoundingMode}. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @param mode + * the {@link RoundingMode} to use + * @return this + */ + public Vector4i set(Vector4dc v, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + this.z = Math.roundUsing(v.z(), mode); + this.w = Math.roundUsing(v.w(), mode); + return this; + } + + /** + * Set this {@link Vector4i} to the values of v using the given {@link RoundingMode}. + *

+ * Note that due to the given vector v storing the components + * in double-precision, there is the possibility to lose precision. + * + * @param v + * the vector to copy from + * @param mode + * the {@link RoundingMode} to use + * @return this + */ + public Vector4i set(Vector4fc v, int mode) { + this.x = Math.roundUsing(v.x(), mode); + this.y = Math.roundUsing(v.y(), mode); + this.z = Math.roundUsing(v.z(), mode); + this.w = Math.roundUsing(v.w(), mode); + return this; + } + + /** + * Set the first three components of this to the components of + * v and the last component to w. + * + * @param v + * the {@link Vector3ic} to copy + * @param w + * the w component + * @return this + */ + public Vector4i set(Vector3ic v, int w) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + this.w = w; + return this; + } + + /** + * Sets the first two components of this to the components of given + * v and last two components to the given z, and + * w. + * + * @param v + * the {@link Vector2ic} + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4i set(Vector2ic v, int z, int w) { + this.x = v.x(); + this.y = v.y(); + this.z = z; + this.w = w; + return this; + } + + /** + * Set the x, y, z, and w components to the supplied value. + * + * @param s + * the value of all four components + * @return this + */ + public Vector4i set(int s) { + this.x = s; + this.y = s; + this.z = s; + this.w = s; + return this; + } + + /** + * Set the x, y, z, and w components to the supplied values. + * + * @param x + * the x component + * @param y + * the y component + * @param z + * the z component + * @param w + * the w component + * @return this + */ + public Vector4i set(int x, int y, int z, int w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + /** + * Set the four components of this vector to the first four elements of the given array. + * + * @param xyzw + * the array containing at least four elements + * @return this + */ + public Vector4i set(int[] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * read, use {@link #set(int, ByteBuffer)}, taking the absolute position as + * parameter. + * + * @see #set(int, ByteBuffer) + * + * @param buffer + * values will be read in x, y, z, w order + * @return this + */ + public Vector4i set(ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link ByteBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * values will be read in x, y, z, w order + * @return this + */ + public Vector4i set(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Read this vector from the supplied {@link IntBuffer} at the current + * buffer {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * read, use {@link #set(int, IntBuffer)}, taking the absolute position as + * parameter. + * + * @see #set(int, IntBuffer) + * + * @param buffer + * values will be read in x, y, z, w order + * @return this + */ + public Vector4i set(IntBuffer buffer) { + MemUtil.INSTANCE.get(this, buffer.position(), buffer); + return this; + } + + /** + * Read this vector from the supplied {@link IntBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param buffer + * values will be read in x, y, z, w order + * @return this + */ + public Vector4i set(int index, IntBuffer buffer) { + MemUtil.INSTANCE.get(this, index, buffer); + return this; + } + + /** + * Set the values of this vector by reading 4 integer values from off-heap memory, + * starting at the given address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap memory address to read the vector values from + * @return this + */ + public Vector4i setFromAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.get(this, address); + return this; + } + + public int get(int component) throws IllegalArgumentException { + switch (component) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IllegalArgumentException(); + } + } + + public int maxComponent() { + int absX = Math.abs(x); + int absY = Math.abs(y); + int absZ = Math.abs(z); + int absW = Math.abs(w); + if (absX >= absY && absX >= absZ && absX >= absW) { + return 0; + } else if (absY >= absZ && absY >= absW) { + return 1; + } else if (absZ >= absW) { + return 2; + } + return 3; + } + + public int minComponent() { + int absX = Math.abs(x); + int absY = Math.abs(y); + int absZ = Math.abs(z); + int absW = Math.abs(w); + if (absX < absY && absX < absZ && absX < absW) { + return 0; + } else if (absY < absZ && absY < absW) { + return 1; + } else if (absZ < absW) { + return 2; + } + return 3; + } + + /** + * Set the value of the specified component of this vector. + * + * @param component + * the component whose value to set, within [0..3] + * @param value + * the value to set + * @return this + * @throws IllegalArgumentException if component is not within [0..3] + */ + public Vector4i setComponent(int component, int value) throws IllegalArgumentException { + switch (component) { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + case 3: + w = value; + break; + default: + throw new IllegalArgumentException(); + } + return this; + } + + public IntBuffer get(IntBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public IntBuffer get(int index, IntBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public ByteBuffer get(ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, buffer.position(), buffer); + return buffer; + } + + public ByteBuffer get(int index, ByteBuffer buffer) { + MemUtil.INSTANCE.put(this, index, buffer); + return buffer; + } + + public Vector4ic getToAddress(long address) { + if (Options.NO_UNSAFE) + throw new UnsupportedOperationException("Not supported when using joml.nounsafe"); + MemUtil.MemUtilUnsafe.put(this, address); + return this; + } + + /** + * Subtract the supplied vector from this one. + * + * @param v + * the vector to subtract + * @return this + */ + public Vector4i sub(Vector4ic v) { + this.x = this.x - v.x(); + this.y = this.y - v.y(); + this.z = this.z - v.z(); + this.w = this.w - v.w(); + return this; + } + + /** + * Subtract (x, y, z, w) from this. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param w + * the w component to subtract + * @return this + */ + public Vector4i sub(int x, int y, int z, int w) { + this.x = this.x - x; + this.y = this.y - y; + this.z = this.z - z; + this.w = this.w - w; + return this; + } + + public Vector4i sub(Vector4ic v, Vector4i dest) { + dest.x = this.x - v.x(); + dest.y = this.y - v.y(); + dest.z = this.z - v.z(); + dest.w = this.w - v.w(); + return dest; + } + + public Vector4i sub(int x, int y, int z, int w, Vector4i dest) { + dest.x = this.x - x; + dest.y = this.y - y; + dest.z = this.z - z; + dest.w = this.w - w; + return dest; + } + + /** + * Add the supplied vector to this one. + * + * @param v + * the vector to add + * @return this + */ + public Vector4i add(Vector4ic v) { + this.x = this.x + v.x(); + this.y = this.y + v.y(); + this.z = this.z + v.z(); + this.w = this.w + v.w(); + return this; + } + + public Vector4i add(Vector4ic v, Vector4i dest) { + dest.x = this.x + v.x(); + dest.y = this.y + v.y(); + dest.z = this.z + v.z(); + dest.w = this.w + v.w(); + return dest; + } + + /** + * Increment the components of this vector by the given values. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @param w + * the w component to add + * @return this + */ + public Vector4i add(int x, int y, int z, int w) { + this.x = this.x + x; + this.y = this.y + y; + this.z = this.z + z; + this.w = this.w + w; + return this; + } + + public Vector4i add(int x, int y, int z, int w, Vector4i dest) { + dest.x = this.x + x; + dest.y = this.y + y; + dest.z = this.z + z; + dest.w = this.w + w; + return dest; + } + + /** + * Multiply this Vector4i component-wise by another Vector4i. + * + * @param v + * the other vector + * @return this + */ + public Vector4i mul(Vector4ic v) { + this.x = x * v.x(); + this.y = y * v.y(); + this.z = z * v.z(); + this.w = w * v.w(); + return this; + } + + public Vector4i mul(Vector4ic v, Vector4i dest) { + dest.x = x * v.x(); + dest.y = y * v.y(); + dest.z = z * v.z(); + dest.w = w * v.w(); + return dest; + } + + /** + * Divide this Vector4i component-wise by another Vector4i. + * + * @param v + * the vector to divide by + * @return this + */ + public Vector4i div(Vector4ic v) { + this.x = x / v.x(); + this.y = y / v.y(); + this.z = z / v.z(); + this.w = w / v.w(); + return this; + } + + public Vector4i div(Vector4ic v, Vector4i dest) { + dest.x = x / v.x(); + dest.y = y / v.y(); + dest.z = z / v.z(); + dest.w = w / v.w(); + return dest; + } + + /** + * Multiply all components of this {@link Vector4i} by the given scalar + * value. + * + * @param scalar + * the scalar to multiply by + * @return this + */ + public Vector4i mul(int scalar) { + this.x = x * scalar; + this.y = y * scalar; + this.z = z * scalar; + this.w = w * scalar; + return this; + } + + public Vector4i mul(int scalar, Vector4i dest) { + dest.x = x * scalar; + dest.y = y * scalar; + dest.z = z * scalar; + dest.w = w * scalar; + return dest; + } + + /** + * Divide all components of this {@link Vector3i} by the given scalar value. + * + * @param scalar + * the scalar to divide by + * @return this + */ + public Vector4i div(float scalar) { + float invscalar = 1.0f / scalar; + this.x = (int) (x * invscalar); + this.y = (int) (y * invscalar); + this.z = (int) (z * invscalar); + this.w = (int) (w * invscalar); + return this; + } + + public Vector4i div(float scalar, Vector4i dest) { + float invscalar = 1.0f / scalar; + dest.x = (int) (x * invscalar); + dest.y = (int) (y * invscalar); + dest.z = (int) (z * invscalar); + dest.w = (int) (w * invscalar); + return dest; + } + + /** + * Divide all components of this {@link Vector4i} by the given scalar value. + * + * @param scalar + * the scalar to divide by + * @return this + */ + public Vector4i div(int scalar) { + this.x = x / scalar; + this.y = y / scalar; + this.z = z / scalar; + this.w = w / scalar; + return this; + } + + public Vector4i div(int scalar, Vector4i dest) { + dest.x = x / scalar; + dest.y = y / scalar; + dest.z = z / scalar; + dest.w = w / scalar; + return dest; + } + + public long lengthSquared() { + return x * x + y * y + z * z + w * w; + } + + /** + * Get the length squared of a 4-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * @param w The vector's w component + * + * @return the length squared of the given vector + */ + public static long lengthSquared(int x, int y, int z, int w) { + return x * x + y * y + z * z + w * w; + } + + public double length() { + return Math.sqrt(x * x + y * y + z * z + w * w); + } + + /** + * Get the length of a 4-dimensional single-precision vector. + * + * @param x The vector's x component + * @param y The vector's y component + * @param z The vector's z component + * @param w The vector's w component + * + * @return the length squared of the given vector + */ + public static double length(int x, int y, int z, int w) { + return Math.sqrt(x * x + y * y + z * z + w * w); + } + + public double distance(Vector4ic v) { + int dx = this.x - v.x(); + int dy = this.y - v.y(); + int dz = this.z - v.z(); + int dw = this.w - v.w(); + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw)))); + } + + public double distance(int x, int y, int z, int w) { + int dx = this.x - x; + int dy = this.y - y; + int dz = this.z - z; + int dw = this.w - w; + return Math.sqrt(Math.fma(dx, dx, Math.fma(dy, dy, Math.fma(dz, dz, dw * dw)))); + } + + public long gridDistance(Vector4ic v) { + return Math.abs(v.x() - x()) + Math.abs(v.y() - y()) + Math.abs(v.z() - z()) + Math.abs(v.w() - w()); + } + + public long gridDistance(int x, int y, int z, int w) { + return Math.abs(x - x()) + Math.abs(y - y()) + Math.abs(z - z()) + Math.abs(w - w()); + } + + public int distanceSquared(Vector4ic v) { + int dx = this.x - v.x(); + int dy = this.y - v.y(); + int dz = this.z - v.z(); + int dw = this.w - v.w(); + return dx * dx + dy * dy + dz * dz + dw * dw; + } + + public int distanceSquared(int x, int y, int z, int w) { + int dx = this.x - x; + int dy = this.y - y; + int dz = this.z - z; + int dw = this.w - w; + return dx * dx + dy * dy + dz * dz + dw * dw; + } + + /** + * Return the distance between (x1, y1, z1, w1) and (x2, y2, z2, w2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param w1 + * the w component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @param w2 + * the 2 component of the second vector + * @return the euclidean distance + */ + public static double distance(int x1, int y1, int z1, int w1, int x2, int y2, int z2, int w2) { + int dx = x1 - x2; + int dy = y1 - y2; + int dz = z1 - z2; + int dw = w1 - w2; + return Math.sqrt(dx * dx + dy * dy + dz * dz + dw * dw); + } + + /** + * Return the squared distance between (x1, y1, z1, w1) and (x2, y2, z2, w2). + * + * @param x1 + * the x component of the first vector + * @param y1 + * the y component of the first vector + * @param z1 + * the z component of the first vector + * @param w1 + * the w component of the first vector + * @param x2 + * the x component of the second vector + * @param y2 + * the y component of the second vector + * @param z2 + * the z component of the second vector + * @param w2 + * the w component of the second vector + * @return the euclidean distance squared + */ + public static long distanceSquared(int x1, int y1, int z1, int w1, int x2, int y2, int z2, int w2) { + int dx = x1 - x2; + int dy = y1 - y2; + int dz = z1 - z2; + int dw = w1 - w2; + return dx * dx + dy * dy + dz * dz + dw * dw; + } + + public int dot(Vector4ic v) { + return x * v.x() + y * v.y() + z * v.z() + w * v.w(); + } + + /** + * Set all components to zero. + * + * @return this + */ + public Vector4i zero() { + x = 0; + y = 0; + z = 0; + w = 0; + return this; + } + + /** + * Negate this vector. + * + * @return this + */ + public Vector4i negate() { + this.x = -x; + this.y = -y; + this.z = -z; + this.w = -w; + return this; + } + + public Vector4i negate(Vector4i dest) { + dest.x = -x; + dest.y = -y; + dest.z = -z; + dest.w = -w; + return dest; + } + + /** + * Return a string representation of this vector. + *

+ * This method creates a new {@link DecimalFormat} on every invocation with the format string "0.000E0;-". + * + * @return the string representation + */ + public String toString() { + return Runtime.formatNumbers(toString(Options.NUMBER_FORMAT)); + } + + /** + * Return a string representation of this vector by formatting the vector components with the given {@link NumberFormat}. + * + * @param formatter + * the {@link NumberFormat} used to format the vector components with + * @return the string representation + */ + public String toString(NumberFormat formatter) { + return "(" + formatter.format(x) + " " + formatter.format(y) + " " + formatter.format(z) + " " + formatter.format(w) + ")"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(x); + out.writeInt(y); + out.writeInt(z); + out.writeInt(w); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + x = in.readInt(); + y = in.readInt(); + z = in.readInt(); + w = in.readInt(); + } + + /** + * Set the components of this vector to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector4i min(Vector4ic v) { + this.x = x < v.x() ? x : v.x(); + this.y = y < v.y() ? y : v.y(); + this.z = z < v.z() ? z : v.z(); + this.w = w < v.w() ? w : v.w(); + return this; + } + + public Vector4i min(Vector4ic v, Vector4i dest) { + dest.x = x < v.x() ? x : v.x(); + dest.y = y < v.y() ? y : v.y(); + dest.z = z < v.z() ? z : v.z(); + dest.w = w < v.w() ? w : v.w(); + return dest; + } + + /** + * Set the components of this vector to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @return this + */ + public Vector4i max(Vector4ic v) { + this.x = x > v.x() ? x : v.x(); + this.y = y > v.y() ? y : v.y(); + this.z = z > v.z() ? z : v.z(); + this.w = w > v.w() ? w : v.w(); + return this; + } + + public Vector4i max(Vector4ic v, Vector4i dest) { + dest.x = x > v.x() ? x : v.x(); + dest.y = y > v.y() ? y : v.y(); + dest.z = z > v.z() ? z : v.z(); + dest.w = w > v.w() ? w : v.w(); + return dest; + } + + /** + * Compute the absolute of each of this vector's components. + * + * @return this + */ + public Vector4i absolute() { + this.x = Math.abs(x); + this.y = Math.abs(y); + this.z = Math.abs(z); + this.w = Math.abs(w); + return this; + } + + public Vector4i absolute(Vector4i dest) { + dest.x = Math.abs(x); + dest.y = Math.abs(y); + dest.z = Math.abs(z); + dest.w = Math.abs(w); + return dest; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + x; + result = prime * result + y; + result = prime * result + z; + result = prime * result + w; + return result; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Vector4i other = (Vector4i) obj; + if (x != other.x) { + return false; + } + if (y != other.y) { + return false; + } + if (z != other.z) { + return false; + } + if (w != other.w) { + return false; + } + return true; + } + + public boolean equals(int x, int y, int z, int w) { + if (this.x != x) + return false; + if (this.y != y) + return false; + if (this.z != z) + return false; + if (this.w != w) + return false; + return true; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4ic.java b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4ic.java new file mode 100644 index 000000000..b4cd5a40b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/Vector4ic.java @@ -0,0 +1,432 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +/** + * Interface to a read-only view of a 4-dimensional vector of integers. + * + * @author Kai Burjack + */ +public interface Vector4ic { + + /** + * @return the value of the x component + */ + int x(); + + /** + * @return the value of the y component + */ + int y(); + + /** + * @return the value of the z component + */ + int z(); + + /** + * @return the value of the w component + */ + int w(); + + /** + * Store this vector into the supplied {@link IntBuffer} at the current + * buffer {@link IntBuffer#position() position}. + *

+ * This method will not increment the position of the given IntBuffer. + *

+ * In order to specify the offset into the IntBuffer at which the vector is + * stored, use {@link #get(int, IntBuffer)}, taking the absolute position as + * parameter. + * + * @see #get(int, IntBuffer) + * + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + IntBuffer get(IntBuffer buffer); + + /** + * Store this vector into the supplied {@link IntBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given IntBuffer. + * + * @param index + * the absolute position into the IntBuffer + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + IntBuffer get(int index, IntBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} at the current + * buffer {@link ByteBuffer#position() position}. + *

+ * This method will not increment the position of the given ByteBuffer. + *

+ * In order to specify the offset into the ByteBuffer at which the vector is + * stored, use {@link #get(int, ByteBuffer)}, taking the absolute position + * as parameter. + * + * @see #get(int, ByteBuffer) + * + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + ByteBuffer get(ByteBuffer buffer); + + /** + * Store this vector into the supplied {@link ByteBuffer} starting at the + * specified absolute buffer position/index. + *

+ * This method will not increment the position of the given ByteBuffer. + * + * @param index + * the absolute position into the ByteBuffer + * @param buffer + * will receive the values of this vector in x, y, z, w order + * @return the passed in buffer + */ + ByteBuffer get(int index, ByteBuffer buffer); + + /** + * Store this vector at the given off-heap memory address. + *

+ * This method will throw an {@link UnsupportedOperationException} when JOML is used with `-Djoml.nounsafe`. + *

+ * This method is unsafe as it can result in a crash of the JVM process when the specified address range does not belong to this process. + * + * @param address + * the off-heap address where to store this vector + * @return this + */ + Vector4ic getToAddress(long address); + + /** + * Subtract the supplied vector from this one and store the result in + * dest. + * + * @param v + * the vector to subtract from this + * @param dest + * will hold the result + * @return dest + */ + Vector4i sub(Vector4ic v, Vector4i dest); + + /** + * Subtract (x, y, z, w) from this and store the result in + * dest. + * + * @param x + * the x component to subtract + * @param y + * the y component to subtract + * @param z + * the z component to subtract + * @param w + * the w component to subtract + * @param dest + * will hold the result + * @return dest + */ + Vector4i sub(int x, int y, int z, int w, Vector4i dest); + + /** + * Add the supplied vector to this one and store the result in + * dest. + * + * @param v + * the vector to add + * @param dest + * will hold the result + * @return dest + */ + Vector4i add(Vector4ic v, Vector4i dest); + + /** + * Increment the components of this vector by the given values and store the + * result in dest. + * + * @param x + * the x component to add + * @param y + * the y component to add + * @param z + * the z component to add + * @param w + * the w component to add + * @param dest + * will hold the result + * @return dest + */ + Vector4i add(int x, int y, int z, int w, Vector4i dest); + + /** + * Multiply this Vector4i component-wise by another Vector4ic and store the + * result in dest. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector4i mul(Vector4ic v, Vector4i dest); + + /** + * Divide this Vector4i component-wise by another Vector4ic and store the + * result in dest. + * + * @param v + * the vector to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector4i div(Vector4ic v, Vector4i dest); + + /** + * Multiply all components of this {@link Vector4i} by the given scalar + * value and store the result in dest. + * + * @param scalar + * the scalar to multiply by + * @param dest + * will hold the result + * @return dest + */ + Vector4i mul(int scalar, Vector4i dest); + + /** + * Divide all components of this {@link Vector4i} by the given scalar value + * and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector4i div(float scalar, Vector4i dest); + + /** + * Divide all components of this {@link Vector4i} by the given scalar value + * and store the result in dest. + * + * @param scalar + * the scalar to divide by + * @param dest + * will hold the result + * @return dest + */ + Vector4i div(int scalar, Vector4i dest); + + /** + * Return the length squared of this vector. + * + * @return the length squared + */ + long lengthSquared(); + + /** + * Return the length of this vector. + * + * @return the length + */ + double length(); + + /** + * Return the distance between this Vector and v. + * + * @param v + * the other vector + * @return the distance + */ + double distance(Vector4ic v); + + /** + * Return the distance between this vector and (x, y, z, w). + * + * @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 euclidean distance + */ + double distance(int x, int y, int z, int w); + + /** + * Return the grid distance in between (aka 1-Norm, Minkowski or Manhattan distance) + * (x, y). + * + * @param v + * the other vector + * @return the grid distance + */ + long gridDistance(Vector4ic v); + + /** + * Return the grid distance in between (aka 1-Norm, Minkowski or Manhattan distance) + * (x, y). + * + * @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 grid distance + */ + long gridDistance(int x, int y, int z, int w); + + /** + * Return the square of the distance between this vector and v. + * + * @param v + * the other vector + * @return the squared of the distance + */ + int distanceSquared(Vector4ic v); + + /** + * Return the square of the distance between this vector and + * (x, y, z, w). + * + * @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 square of the distance + */ + int distanceSquared(int x, int y, int z, int w); + + /** + * Compute the dot product (inner product) of this vector and v. + * + * @param v + * the other vector + * @return the dot product + */ + int dot(Vector4ic v); + + /** + * Negate this vector and store the result in dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4i negate(Vector4i dest); + + /** + * Set the components of dest to be the component-wise minimum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector4i min(Vector4ic v, Vector4i dest); + + /** + * Set the components of dest to be the component-wise maximum of this and the other vector. + * + * @param v + * the other vector + * @param dest + * will hold the result + * @return dest + */ + Vector4i max(Vector4ic v, Vector4i dest); + + /** + * Get the value of the specified component of this vector. + * + * @param component + * the component, within [0..3] + * @return the value + * @throws IllegalArgumentException if component is not within [0..3] + */ + int get(int component) throws IllegalArgumentException; + + /** + * Determine the component with the biggest absolute value. + * + * @return the component index, within [0..3] + */ + int maxComponent(); + + /** + * Determine the component with the smallest (towards zero) absolute value. + * + * @return the component index, within [0..3] + */ + int minComponent(); + + /** + * Compute the absolute of each of this vector's components + * and store the result into dest. + * + * @param dest + * will hold the result + * @return dest + */ + Vector4i absolute(Vector4i dest); + + /** + * Compare the vector components of this vector with the given (x, y, z, w) + * and return whether all of them are equal. + * + * @param x + * the x component to compare to + * @param y + * the y component to compare to + * @param z + * the z component to compare to + * @param w + * the w component to compare to + * @return true if all the vector components are equal + */ + boolean equals(int x, int y, int z, int w); + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/package.html b/src/main/java/com/jozufozu/flywheel/repack/joml/package.html new file mode 100644 index 000000000..02a84240d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/package.html @@ -0,0 +1,14 @@ + + + + + +

Contains all classes of JOML.

+

If you like to know more about JOML, please visit:

+ + + diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/BestCandidateSampling.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/BestCandidateSampling.java new file mode 100644 index 000000000..07a0eb925 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/BestCandidateSampling.java @@ -0,0 +1,1034 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +import java.nio.FloatBuffer; +import java.util.ArrayList; + +import com.jozufozu.flywheel.repack.joml.Random; +import com.jozufozu.flywheel.repack.joml.Vector2f; +import com.jozufozu.flywheel.repack.joml.Vector3f; + +/** + * Creates samples using the "Best Candidate" algorithm. + * + * @author Kai Burjack + */ +public class BestCandidateSampling { + + private static final class IntHolder { + int value; + } + + /** + * Generates Best Candidate samples on a unit sphere. + *

+ * References: + *

+ * + * @author Kai Burjack + */ + public static class Sphere { + /** + * Implementation of a Hierarchical Triangular Mesh structure to index the sample points on the unit sphere for accelerating 1-nearest neighbor searches. + * + * @author Kai Burjack + */ + private static final class Node { + private static final int MAX_OBJECTS_PER_NODE = 32; + + private float v0x, v0y, v0z; + private float v1x, v1y, v1z; + private float v2x, v2y, v2z; + private float cx, cy, cz; + private float arc; + + private ArrayList objects; + private Node[] children; + + Node() { + this.children = new Node[8]; + float s = 1f; + this.arc = 2.0f * (float) Math.PI; + /* + * See: https://arxiv.org/ftp/cs/papers/0701/0701164.pdf + */ + this.children[0] = new Node(-s, 0, 0, 0, 0, s, 0, s, 0); + this.children[1] = new Node(0, 0, s, s, 0, 0, 0, s, 0); + this.children[2] = new Node(s, 0, 0, 0, 0, -s, 0, s, 0); + this.children[3] = new Node(0, 0, -s, -s, 0, 0, 0, s, 0); + this.children[4] = new Node(-s, 0, 0, 0, -s, 0, 0, 0, s); + this.children[5] = new Node(0, 0, s, 0, -s, 0, s, 0, 0); + this.children[6] = new Node(s, 0, 0, 0, -s, 0, 0, 0, -s); + this.children[7] = new Node(0, 0, -s, 0, -s, 0, -s, 0, 0); + } + + private Node(float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2) { + this.v0x = x0; + this.v0y = y0; + this.v0z = z0; + this.v1x = x1; + this.v1y = y1; + this.v1z = z1; + this.v2x = x2; + this.v2y = y2; + this.v2z = z2; + cx = (v0x + v1x + v2x) / 3.0f; + cy = (v0y + v1y + v2y) / 3.0f; + cz = (v0z + v1z + v2z) / 3.0f; + float invCLen = Math.invsqrt(cx * cx + cy * cy + cz * cz); + cx *= invCLen; + cy *= invCLen; + cz *= invCLen; + // Compute maximum radius around triangle centroid + float arc1 = greatCircleDist(cx, cy, cz, v0x, v0y, v0z); + float arc2 = greatCircleDist(cx, cy, cz, v1x, v1y, v1z); + float arc3 = greatCircleDist(cx, cy, cz, v2x, v2y, v2z); + float dist = Math.max(Math.max(arc1, arc2), arc3); + /* + * Convert radius into diameter, but also take into account the linear + * arccos approximation we use. + * This value was obtained empirically! + */ + dist *= 1.7f; + this.arc = dist; + } + + private void split() { + float w0x = v1x + v2x; + float w0y = v1y + v2y; + float w0z = v1z + v2z; + float len0 = Math.invsqrt(w0x * w0x + w0y * w0y + w0z * w0z); + w0x *= len0; + w0y *= len0; + w0z *= len0; + float w1x = v0x + v2x; + float w1y = v0y + v2y; + float w1z = v0z + v2z; + float len1 = Math.invsqrt(w1x * w1x + w1y * w1y + w1z * w1z); + w1x *= len1; + w1y *= len1; + w1z *= len1; + float w2x = v0x + v1x; + float w2y = v0y + v1y; + float w2z = v0z + v1z; + float len2 = Math.invsqrt(w2x * w2x + w2y * w2y + w2z * w2z); + w2x *= len2; + w2y *= len2; + w2z *= len2; + children = new Node[4]; + /* + * See: https://arxiv.org/ftp/cs/papers/0701/0701164.pdf + */ + children[0] = new Node(v0x, v0y, v0z, w2x, w2y, w2z, w1x, w1y, w1z); + children[1] = new Node(v1x, v1y, v1z, w0x, w0y, w0z, w2x, w2y, w2z); + children[2] = new Node(v2x, v2y, v2z, w1x, w1y, w1z, w0x, w0y, w0z); + children[3] = new Node(w0x, w0y, w0z, w1x, w1y, w1z, w2x, w2y, w2z); + } + + private void insertIntoChild(Vector3f o) { + for (int i = 0; i < children.length; i++) { + Node c = children[i]; + /* + * Idea: Test whether ray from origin towards point cuts triangle + * + * See: http://math.stackexchange.com/questions/1244512/point-in-a-spherical-triangle-test + */ + if (isPointOnSphericalTriangle(o.x, o.y, o.z, c.v0x, c.v0y, c.v0z, c.v1x, c.v1y, c.v1z, c.v2x, c.v2y, c.v2z, 1E-6f)) { + c.insert(o); + return; + } + } + } + + void insert(Vector3f object) { + if (children != null) { + insertIntoChild(object); + return; + } + if (objects != null && objects.size() == MAX_OBJECTS_PER_NODE) { + split(); + for (int i = 0; i < MAX_OBJECTS_PER_NODE; i++) + insertIntoChild((Vector3f) objects.get(i)); + objects = null; + insertIntoChild(object); + } else { + if (objects == null) + objects = new ArrayList(MAX_OBJECTS_PER_NODE); + objects.add(object); + } + } + + /** + * This is essentially a ray cast from the origin of the sphere to the point to test and then checking whether that ray goes through the triangle. + *

+ * Reference: Fast, + * Minimum Storage Ray/Triangle Intersection + */ + private static boolean isPointOnSphericalTriangle(float x, float y, float z, float v0X, float v0Y, float v0Z, float v1X, float v1Y, float v1Z, float v2X, float v2Y, + float v2Z, float epsilon) { + float edge1X = v1X - v0X; + float edge1Y = v1Y - v0Y; + float edge1Z = v1Z - v0Z; + float edge2X = v2X - v0X; + float edge2Y = v2Y - v0Y; + float edge2Z = v2Z - v0Z; + float pvecX = y * edge2Z - z * edge2Y; + float pvecY = z * edge2X - x * edge2Z; + float pvecZ = x * edge2Y - y * edge2X; + float det = edge1X * pvecX + edge1Y * pvecY + edge1Z * pvecZ; + if (det > -epsilon && det < epsilon) + return false; + float tvecX = -v0X; + float tvecY = -v0Y; + float tvecZ = -v0Z; + float invDet = 1.0f / det; + float u = (tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ) * invDet; + if (u < 0.0f || u > 1.0f) + return false; + float qvecX = tvecY * edge1Z - tvecZ * edge1Y; + float qvecY = tvecZ * edge1X - tvecX * edge1Z; + float qvecZ = tvecX * edge1Y - tvecY * edge1X; + float v = (x * qvecX + y * qvecY + z * qvecZ) * invDet; + if (v < 0.0f || u + v > 1.0f) + return false; + float t = (edge2X * qvecX + edge2Y * qvecY + edge2Z * qvecZ) * invDet; + return t >= epsilon; + } + + private int child(float x, float y, float z) { + for (int i = 0; i < children.length; i++) { + Node c = children[i]; + if (isPointOnSphericalTriangle(x, y, z, c.v0x, c.v0y, c.v0z, c.v1x, c.v1y, c.v1z, c.v2x, c.v2y, c.v2z, 1E-5f)) + return i; + } + // No child found. This can happen in 'nearest()' when querying possible nearby nodes + return 0; + } + + /** + * Reference: https://en.wikipedia.org/ + */ + private float greatCircleDist(float x1, float y1, float z1, float x2, float y2, float z2) { + float dot = x1 * x2 + y1 * y2 + z1 * z2; + /* + * Just use a linear function, because we (mostly) do less-than comparisons on the result. + * We just need a linear function which: + * f(-1) = PI + * f(0) = PI/2 + * f(1) = 0 + */ + return (float) (-Math.PIHalf * dot + Math.PIHalf); + //return (float) Math.acos(dot); + } + + float nearest(float x, float y, float z) { + return nearest(x, y, z, Float.POSITIVE_INFINITY); + } + float nearest(float x, float y, float z, float n) { + float gcd = greatCircleDist(x, y, z, cx, cy, cz); + /* + * If great-circle-distance between query point and centroid is larger than the current smallest distance 'n' plus the great circle diameter 'arc', we abort here, + * because then it is not possible for any point in the triangle patch to be closer to the query point than 'n'. + */ + /* + * Yes, we are subtracting two great-circle distances from one another here, which we did not even compute correctly + * using our overly linear arccos approximation. But the 1.7 factor above will take care that we still stay conservative + * enough here and not rejecting triangle patches which would contain samples nearer than 'n'. + */ + if (gcd - arc > n) + return n; + float nr = n; + if (children != null) { + int num = children.length, mod = num-1; + for (int i = child(x, y, z), c = 0; c < num; i = (i + 1) & mod, c++) { + float n1 = children[i].nearest(x, y, z, nr); + nr = Math.min(n1, nr); + } + return nr; + } + for (int i = 0; objects != null && i < objects.size(); i++) { + Vector3f o = (Vector3f) objects.get(i); + float d = greatCircleDist(o.x, o.y, o.z, x, y, z); + if (d < nr) + nr = d; + } + return nr; + } + } + + private boolean onHemisphere; + private int numSamples; + private int numCandidates = 60; // <- use a reasonable default + private long seed; + + /** + * Create a new instance of {@link Sphere} to configure and generate 'best candidate' sample positions on the unit sphere. + */ + public Sphere() {} + + /** + * Generate 'best candidate' sample positions and store the coordinates of all generated samples into the given xyzs float array. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param xyzs + * will hold the x, y and z coordinates of all samples in the order XYZXYZXYZ.... + * This array must have a length of at least numSamples + * @return this + */ + public Sphere generate(final float[] xyzs) { + final IntHolder i = new IntHolder(); + return generate(new Callback3d() { + public void onNewSample(float x, float y, float z) { + xyzs[3 * i.value + 0] = x; + xyzs[3 * i.value + 1] = y; + xyzs[3 * i.value + 2] = z; + i.value++; + } + }); + } + + /** + * Generate 'best candidate' sample positions and store the coordinates of all generated samples into the given xyzs FloatBuffer. + *

+ * The samples will be written starting at the current position of the FloatBuffer. The position of the FloatBuffer will not be modified. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param xyzs + * will hold the x, y and z coordinates of all samples in the order XYZXYZXYZ.... + * This FloatBuffer must have at least numSamples remaining elements. + * The position of the buffer will not be modified by this method + * @return this + */ + public Sphere generate(final FloatBuffer xyzs) { + final IntHolder i = new IntHolder(); + final int pos = xyzs.position(); + return generate(new Callback3d() { + public void onNewSample(float x, float y, float z) { + xyzs.put(pos + 3 * i.value + 0, x); + xyzs.put(pos + 3 * i.value + 1, y); + xyzs.put(pos + 3 * i.value + 2, z); + i.value++; + } + }); + } + + /** + * Set the seed to initialize the pseudo-random number generator with. + * + * @param seed + * the seed value + * @return this + */ + public Sphere seed(long seed) { + this.seed = seed; + return this; + } + + /** + * Set the number of samples to generate. + * + * @param numSamples + * the number of samples + * @return this + */ + public Sphere numSamples(int numSamples) { + this.numSamples = numSamples; + return this; + } + + /** + * Set the number of candidates to try for each generated sample. + * + * @param numCandidates + * the number of candidates to try + * @return this + */ + public Sphere numCandidates(int numCandidates) { + this.numCandidates = numCandidates; + return this; + } + + /** + * Set whether to generate samples on a hemisphere around the +Z axis. + *

+ * The default is false, which will generate samples on the whole unit sphere. + * + * @param onHemisphere + * whether to generate samples on the hemisphere + * @return this + */ + public Sphere onHemisphere(boolean onHemisphere) { + this.onHemisphere = onHemisphere; + return this; + } + + /** + * Generate 'best candidate' sample call the given callback for each generated sample. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param callback + * will be called with the coordinates of each generated sample position + * @return this + */ + public Sphere generate(Callback3d callback) { + Random rnd = new Random(seed); + Node otree = new Node(); + for (int i = 0; i < numSamples; i++) { + float bestX = Float.NaN, bestY = Float.NaN, bestZ = Float.NaN, bestDist = 0.0f; + for (int c = 0; c < numCandidates; c++) { + /* + * Random point on sphere + * + * Reference: http://mathworld.wolfram.com/ + */ + float x1, x2; + do { + x1 = rnd.nextFloat() * 2.0f - 1.0f; + x2 = rnd.nextFloat() * 2.0f - 1.0f; + } while (x1 * x1 + x2 * x2 > 1.0f); + float sqrt = (float) Math.sqrt(1.0 - x1 * x1 - x2 * x2); + float x = 2 * x1 * sqrt; + float y = 2 * x2 * sqrt; + float z = 1.0f - 2.0f * (x1 * x1 + x2 * x2); + if (onHemisphere) { + z = Math.abs(z); + } + float minDist = otree.nearest(x, y, z); + if (minDist > bestDist) { + bestDist = minDist; + bestX = x; + bestY = y; + bestZ = z; + } + } + callback.onNewSample(bestX, bestY, bestZ); + otree.insert(new Vector3f(bestX, bestY, bestZ)); + } + return this; + } + } + + /** + * Simple quatree that can handle points and 1-nearest neighbor search. + * + * @author Kai Burjack + */ + private static class QuadTree { + private static final int MAX_OBJECTS_PER_NODE = 32; + + // Constants for the quadrants of the quadtree + private static final int PXNY = 0; + private static final int NXNY = 1; + private static final int NXPY = 2; + private static final int PXPY = 3; + + private float minX, minY, hs; + private ArrayList objects; + private QuadTree[] children; + + QuadTree(float minX, float minY, float size) { + this.minX = minX; + this.minY = minY; + this.hs = size * 0.5f; + } + + private void split() { + children = new QuadTree[4]; + children[NXNY] = new QuadTree(minX, minY, hs); + children[PXNY] = new QuadTree(minX + hs, minY, hs); + children[NXPY] = new QuadTree(minX, minY + hs, hs); + children[PXPY] = new QuadTree(minX + hs, minY + hs, hs); + } + + private void insertIntoChild(Vector2f o) { + children[quadrant(o.x, o.y)].insert(o); + } + + void insert(Vector2f object) { + if (children != null) { + insertIntoChild(object); + return; + } + if (objects != null && objects.size() == MAX_OBJECTS_PER_NODE) { + split(); + for (int i = 0; i < objects.size(); i++) + insertIntoChild((Vector2f) objects.get(i)); + objects = null; + insertIntoChild(object); + } else { + if (objects == null) + objects = new ArrayList(MAX_OBJECTS_PER_NODE); + objects.add(object); + } + } + + private int quadrant(float x, float y) { + if (x < minX + hs) { + if (y < minY + hs) + return NXNY; + return NXPY; + } + if (y < minY + hs) + return PXNY; + return PXPY; + } + + float nearest(float x, float y, float lowerBound, float upperBound) { + float ub = upperBound; + if (x < minX - upperBound || x > minX + hs * 2 + upperBound || y < minY - upperBound + || y > minY + hs * 2 + upperBound) + return ub; + if (children != null) { + for (int i = quadrant(x, y), c = 0; c < 4; i = (i + 1) & 3, c++) { + float n1 = children[i].nearest(x, y, lowerBound, ub); + ub = Math.min(n1, ub); + if (ub <= lowerBound) + return lowerBound; + } + return ub; + } + float ub2 = ub * ub; + float lb2 = lowerBound * lowerBound; + for (int i = 0; objects != null && i < objects.size(); i++) { + Vector2f o = (Vector2f) objects.get(i); + float d = o.distanceSquared(x, y); + if (d <= lb2) + return lowerBound; + if (d < ub2) + ub2 = d; + } + return (float) Math.sqrt(ub2); + } + } + + /** + * Generates Best Candidate samples on a unit disk. + * + * @author Kai Burjack + */ + public static class Disk { + private int numSamples; + private int numCandidates = 60; // <- use a reasonable default + private long seed; + + /** + * Create a new instance of {@link Disk} to configure and generate 'best candidate' sample positions on the unit disk. + */ + public Disk() {} + + /** + * Set the seed to initialize the pseudo-random number generator with. + * + * @param seed + * the seed value + * @return this + */ + public Disk seed(long seed) { + this.seed = seed; + return this; + } + + /** + * Set the number of samples to generate. + * + * @param numSamples + * the number of samples + * @return this + */ + public Disk numSamples(int numSamples) { + this.numSamples = numSamples; + return this; + } + + /** + * Set the number of candidates to try for each generated sample. + * + * @param numCandidates + * the number of candidates to try + * @return this + */ + public Disk numCandidates(int numCandidates) { + this.numCandidates = numCandidates; + return this; + } + + /** + * Generate 'best candidate' sample positions and store the coordinates of all generated samples into the given xys float array. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param xys + * will hold the x and y coordinates of all samples in the order XYXYXY.... + * This array must have a length of at least numSamples + * @return this + */ + public Disk generate(final float[] xys) { + final IntHolder i = new IntHolder(); + return generate(new Callback2d() { + public void onNewSample(float x, float y) { + xys[2 * i.value + 0] = x; + xys[2 * i.value + 1] = y; + i.value++; + } + }); + } + + /** + * Generate 'best candidate' sample positions and store the coordinates of all generated samples into the given xys FloatBuffer. + *

+ * The samples will be written starting at the current position of the FloatBuffer. The position of the FloatBuffer will not be modified. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param xys + * will hold the x and y coordinates of all samples in the order XYXYXY.... This FloatBuffer must have at least numSamples remaining elements. The + * position of the buffer will not be modified by this method + * @return this + */ + public Disk generate(final FloatBuffer xys) { + final IntHolder i = new IntHolder(); + final int pos = xys.position(); + return generate(new Callback2d() { + public void onNewSample(float x, float y) { + xys.put(pos + 3 * i.value + 0, x); + xys.put(pos + 3 * i.value + 1, y); + i.value++; + } + }); + } + + /** + * Generate 'best candidate' sample positions and call the given callback for each generated sample. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param callback + * will be called with the coordinates of each generated sample position + * @return this + */ + public Disk generate(Callback2d callback) { + QuadTree qtree = new QuadTree(-1, -1, 2); + Random rnd = new Random(seed); + for (int i = 0; i < numSamples; i++) { + float bestX = 0, bestY = 0, bestDist = 0.0f; + for (int c = 0; c < numCandidates; c++) { + float x, y; + do { + x = rnd.nextFloat() * 2.0f - 1.0f; + y = rnd.nextFloat() * 2.0f - 1.0f; + } while (x * x + y * y > 1.0f); + float minDist = qtree.nearest(x, y, bestDist, Float.POSITIVE_INFINITY); + if (minDist > bestDist) { + bestDist = minDist; + bestX = x; + bestY = y; + } + } + callback.onNewSample(bestX, bestY); + qtree.insert(new Vector2f(bestX, bestY)); + } + return this; + } + } + + /** + * Generates Best Candidate samples on a unit quad. + * + * @author Kai Burjack + */ + public static class Quad { + private int numSamples; + private int numCandidates = 60; // <- use a reasonable default + private long seed; + + /** + * Create a new instance of {@link Quad} to configure and generate 'best candidate' sample positions on the unit quad. + */ + public Quad() {} + + /** + * Set the seed to initialize the pseudo-random number generator with. + * + * @param seed + * the seed value + * @return this + */ + public Quad seed(long seed) { + this.seed = seed; + return this; + } + + /** + * Set the number of samples to generate. + * + * @param numSamples + * the number of samples + * @return this + */ + public Quad numSamples(int numSamples) { + this.numSamples = numSamples; + return this; + } + + /** + * Set the number of candidates to try for each generated sample. + * + * @param numCandidates + * the number of candidates to try + * @return this + */ + public Quad numCandidates(int numCandidates) { + this.numCandidates = numCandidates; + return this; + } + + /** + * Generate 'best candidate' sample positions and store the coordinates of all generated samples into the given xyzs float array. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param xyzs + * will hold the x, y and z coordinates of all samples in the order XYZXYZXYZ.... + * This array must have a length of at least numSamples + * @return this + */ + public Quad generate(final float[] xyzs) { + final IntHolder i = new IntHolder(); + return generate(new Callback2d() { + public void onNewSample(float x, float y) { + xyzs[2 * i.value + 0] = x; + xyzs[2 * i.value + 1] = y; + i.value++; + } + }); + } + + /** + * Generate 'best candidate' sample positions and store the coordinates of all generated samples into the given xys FloatBuffer. + *

+ * The samples will be written starting at the current position of the FloatBuffer. The position of the FloatBuffer will not be modified. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param xys + * will hold the x and y coordinates of all samples in the order XYXYXY.... This FloatBuffer must have at least numSamples remaining elements. The position of + * the buffer will not be modified by this method + * @return this + */ + public Quad generate(final FloatBuffer xys) { + final IntHolder i = new IntHolder(); + final int pos = xys.position(); + return generate(new Callback2d() { + public void onNewSample(float x, float y) { + xys.put(pos + 3 * i.value + 0, x); + xys.put(pos + 3 * i.value + 1, y); + i.value++; + } + }); + } + + /** + * Generate 'best candidate' sample positions and call the given callback for each generated sample. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param callback + * will be called with the coordinates of each generated sample position + * @return this + */ + public Quad generate(Callback2d callback) { + QuadTree qtree = new QuadTree(-1, -1, 2); + Random rnd = new Random(seed); + for (int i = 0; i < numSamples; i++) { + float bestX = 0, bestY = 0, bestDist = 0.0f; + for (int c = 0; c < numCandidates; c++) { + float x = rnd.nextFloat() * 2.0f - 1.0f; + float y = rnd.nextFloat() * 2.0f - 1.0f; + float minDist = qtree.nearest(x, y, bestDist, Float.POSITIVE_INFINITY); + if (minDist > bestDist) { + bestDist = minDist; + bestX = x; + bestY = y; + } + } + callback.onNewSample(bestX, bestY); + qtree.insert(new Vector2f(bestX, bestY)); + } + return this; + } + } + + /** + * Simple octree for points and 1-nearest neighbor distance query. + * + * @author Kai Burjack + */ + private static class Octree { + private static final int MAX_OBJECTS_PER_NODE = 32; + + // Constants for the octants of the octree + private static final int PXNYNZ = 0; + private static final int NXNYNZ = 1; + private static final int NXPYNZ = 2; + private static final int PXPYNZ = 3; + private static final int PXNYPZ = 4; + private static final int NXNYPZ = 5; + private static final int NXPYPZ = 6; + private static final int PXPYPZ = 7; + + private float minX, minY, minZ, hs; + private ArrayList objects; + private Octree[] children; + + Octree(float minX, float minY, float minZ, float size) { + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.hs = size * 0.5f; + } + + private void split() { + children = new Octree[8]; + children[NXNYNZ] = new Octree(minX, minY, minZ, hs); + children[PXNYNZ] = new Octree(minX + hs, minY, minZ, hs); + children[NXPYNZ] = new Octree(minX, minY + hs, minZ, hs); + children[PXPYNZ] = new Octree(minX + hs, minY + hs, minZ, hs); + children[NXNYPZ] = new Octree(minX, minY, minZ + hs, hs); + children[PXNYPZ] = new Octree(minX + hs, minY, minZ + hs, hs); + children[NXPYPZ] = new Octree(minX, minY + hs, minZ + hs, hs); + children[PXPYPZ] = new Octree(minX + hs, minY + hs, minZ + hs, hs); + } + + private void insertIntoChild(Vector3f o) { + children[octant(o.x, o.y, o.z)].insert(o); + } + + void insert(Vector3f object) { + if (children != null) { + insertIntoChild(object); + return; + } + if (objects != null && objects.size() == MAX_OBJECTS_PER_NODE) { + split(); + for (int i = 0; i < objects.size(); i++) + insertIntoChild((Vector3f) objects.get(i)); + objects = null; + insertIntoChild(object); + } else { + if (objects == null) + objects = new ArrayList(MAX_OBJECTS_PER_NODE); + objects.add(object); + } + } + + private int octant(float x, float y, float z) { + if (x < minX + hs) + if (y < minY + hs) { + if (z < minZ + hs) + return NXNYNZ; + return NXNYPZ; + } else if (z < minZ + hs) + return NXPYNZ; + else + return NXPYPZ; + else if (y < minY + hs) { + if (z < minZ + hs) + return PXNYNZ; + return PXNYPZ; + } else if (z < minZ + hs) + return PXPYNZ; + else + return PXPYPZ; + } + + float nearest(float x, float y, float z, float lowerBound, float upperBound) { + float up = upperBound; + if (x < minX - upperBound || x > minX + hs * 2 + upperBound || y < minY - upperBound || y > minY + hs * 2 + upperBound || + z < minZ - upperBound || z > minZ + hs * 2 + upperBound) + return up; + if (children != null) { + for (int i = octant(x, y, z), c = 0; c < 8; i = (i + 1) & 7, c++) { + float n1 = children[i].nearest(x, y, z, lowerBound, up); + up = Math.min(n1, up); + if (up <= lowerBound) + return lowerBound; + } + return up; + } + float up2 = up * up; + float lb2 = lowerBound * lowerBound; + for (int i = 0; objects != null && i < objects.size(); i++) { + Vector3f o = (Vector3f) objects.get(i); + float d = o.distanceSquared(x, y, z); + if (d <= lb2) + return lowerBound; + if (d < up2) + up2 = d; + } + return (float) Math.sqrt(up2); + } + } + + /** + * Generates Best Candidate samples inside a unit cube. + * + * @author Kai Burjack + */ + public static class Cube { + private int numSamples; + private int numCandidates = 60; // <- use a reasonable default + private long seed; + + /** + * Create a new instance of {@link Cube} to configure and generate 'best candidate' sample positions + * on the unit cube with each sample tried numCandidates number of times, and call the + * given callback for each sample generate. + */ + public Cube() {} + + /** + * Set the seed to initialize the pseudo-random number generator with. + * + * @param seed + * the seed value + * @return this + */ + public Cube seed(long seed) { + this.seed = seed; + return this; + } + + /** + * Set the number of samples to generate. + * + * @param numSamples + * the number of samples + * @return this + */ + public Cube numSamples(int numSamples) { + this.numSamples = numSamples; + return this; + } + + /** + * Set the number of candidates to try for each generated sample. + * + * @param numCandidates + * the number of candidates to try + * @return this + */ + public Cube numCandidates(int numCandidates) { + this.numCandidates = numCandidates; + return this; + } + + /** + * Generate 'best candidate' sample positions and store the coordinates of all generated samples into the given xyzs float array. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param xyzs + * will hold the x, y and z coordinates of all samples in the order XYZXYZXYZ.... + * This array must have a length of at least numSamples + * @return this + */ + public Cube generate(final float[] xyzs) { + final IntHolder i = new IntHolder(); + return generate(new Callback3d() { + public void onNewSample(float x, float y, float z) { + xyzs[3 * i.value + 0] = x; + xyzs[3 * i.value + 1] = y; + xyzs[3 * i.value + 2] = z; + i.value++; + } + }); + } + + /** + * Generate 'best candidate' sample positions and store the coordinates of all generated samples into the given xyzs FloatBuffer. + *

+ * The samples will be written starting at the current position of the FloatBuffer. The position of the FloatBuffer will not be modified. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param xyzs + * will hold the x, y and z coordinates of all samples in the order XYZXYZXYZ.... + * This FloatBuffer must have at least numSamples remaining elements. + * The position of the buffer will not be modified by this method + * @return this + */ + public Cube generate(final FloatBuffer xyzs) { + final IntHolder i = new IntHolder(); + final int pos = xyzs.position(); + return generate(new Callback3d() { + public void onNewSample(float x, float y, float z) { + xyzs.put(pos + 3 * i.value + 0, x); + xyzs.put(pos + 3 * i.value + 1, y); + xyzs.put(pos + 3 * i.value + 2, z); + i.value++; + } + }); + } + + /** + * Generate 'best candidate' sample positions and call the given callback for each generated sample. + *

+ * This method performs heap allocations, so should be used sparingly. + * + * @param callback + * will be called with the coordinates of each generated sample position + * @return this + */ + public Cube generate(Callback3d callback) { + Octree octree = new Octree(-1, -1, -1, 2); + Random rnd = new Random(seed); + for (int i = 0; i < numSamples; i++) { + float bestX = 0, bestY = 0, bestZ = 0, bestDist = 0.0f; + for (int c = 0; c < numCandidates; c++) { + float x = rnd.nextFloat() * 2.0f - 1.0f; + float y = rnd.nextFloat() * 2.0f - 1.0f; + float z = rnd.nextFloat() * 2.0f - 1.0f; + float minDist = octree.nearest(x, y, z, bestDist, Float.POSITIVE_INFINITY); + if (minDist > bestDist) { + bestDist = minDist; + bestX = x; + bestY = y; + bestZ = z; + } + } + callback.onNewSample(bestX, bestY, bestZ); + octree.insert(new Vector3f(bestX, bestY, bestZ)); + } + return this; + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Callback2d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Callback2d.java new file mode 100644 index 000000000..98380ed67 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Callback2d.java @@ -0,0 +1,41 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +/** + * Callback used for notifying about a new generated 2D sample. + * + * @author Kai Burjack + */ +public interface Callback2d { + /** + * Will be called whenever a new sample with the given coordinates (x, y) is generated. + * + * @param x + * the x coordinate of the new sample point + * @param y + * the y coordinate of the new sample point + */ + void onNewSample(float x, float y); +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Callback3d.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Callback3d.java new file mode 100644 index 000000000..a9ff2c306 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Callback3d.java @@ -0,0 +1,43 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +/** + * Callback used for notifying about a new generated 3D sample. + * + * @author Kai Burjack + */ +public interface Callback3d { + /** + * Will be called whenever a new sample with the given coordinates (x, y, z) is generated. + * + * @param x + * the x coordinate of the new sample point + * @param y + * the y coordinate of the new sample point + * @param z + * the z coordinate of the new sample point + */ + void onNewSample(float x, float y, float z); +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Convolution.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Convolution.java new file mode 100644 index 000000000..2410129e7 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Convolution.java @@ -0,0 +1,116 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +import java.nio.FloatBuffer; + +import com.jozufozu.flywheel.repack.joml.Math; + +/** + * Generates various convolution kernels. + * + * @author Kai Burjack + */ +public class Convolution { + + /** + * Generate a Gaussian convolution kernel with the given number of rows and columns, and store + * the factors in row-major order in dest. + * + * @param rows + * the number of rows (must be an odd number) + * @param cols + * the number of columns (must be an odd number) + * @param sigma + * the standard deviation of the filter kernel values + * @param dest + * will hold the kernel factors in row-major order + */ + public static void gaussianKernel(int rows, int cols, float sigma, FloatBuffer dest) { + if ((rows & 1) == 0) { + throw new IllegalArgumentException("rows must be an odd number"); + } + if ((cols & 1) == 0) { + throw new IllegalArgumentException("cols must be an odd number"); + } + if (dest == null) { + throw new IllegalArgumentException("dest must not be null"); + } + if (dest.remaining() < rows * cols) { + throw new IllegalArgumentException("dest must have at least " + (rows * cols) + " remaining values"); + } + float sum = 0.0f; + int pos = dest.position(); + for (int i = 0, y = -(rows - 1) / 2; y <= (rows - 1) / 2; y++) { + for (int x = -(cols - 1) / 2; x <= (cols - 1) / 2; x++, i++) { + float k = (float) Math.exp(-(y * y + x * x) / (2.0 * sigma * sigma)); + dest.put(pos + i, k); + sum += k; + } + } + for (int i = 0; i < rows * cols; i++) { + dest.put(pos + i, dest.get(pos + i) / sum); + } + } + + /** + * Generate a Gaussian convolution kernel with the given number of rows and columns, and store + * the factors in row-major order in dest. + * + * @param rows + * the number of rows (must be an odd number) + * @param cols + * the number of columns (must be an odd number) + * @param sigma + * the standard deviation of the filter kernel values + * @param dest + * will hold the kernel factors in row-major order + */ + public static void gaussianKernel(int rows, int cols, float sigma, float[] dest) { + if ((rows & 1) == 0) { + throw new IllegalArgumentException("rows must be an odd number"); + } + if ((cols & 1) == 0) { + throw new IllegalArgumentException("cols must be an odd number"); + } + if (dest == null) { + throw new IllegalArgumentException("dest must not be null"); + } + if (dest.length < rows * cols) { + throw new IllegalArgumentException("dest must have a size of at least " + (rows * cols)); + } + float sum = 0.0f; + for (int i = 0, y = -(rows - 1) / 2; y <= (rows - 1) / 2; y++) { + for (int x = -(cols - 1) / 2; x <= (cols - 1) / 2; x++, i++) { + float k = (float) Math.exp(-(y * y + x * x) / (2.0 * sigma * sigma)); + dest[i] = k; + sum += k; + } + } + for (int i = 0; i < rows * cols; i++) { + dest[i] = dest[i] / sum; + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Math.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Math.java new file mode 100644 index 000000000..8dc6669e6 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/Math.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +/** + * Internal Math class used by the sampling package. + * + * @author Kai Burjack + */ +class Math extends com.jozufozu.flywheel.repack.joml.Math { + + static final double PI = java.lang.Math.PI; + static final double PI2 = PI * 2.0; + static final double PIHalf = PI * 0.5; + private static final double ONE_OVER_PI = 1.0 / PI; + private static final double s5 = Double.longBitsToDouble(4523227044276562163L); + private static final double s4 = Double.longBitsToDouble(-4671934770969572232L); + private static final double s3 = Double.longBitsToDouble(4575957211482072852L); + private static final double s2 = Double.longBitsToDouble(-4628199223918090387L); + private static final double s1 = Double.longBitsToDouble(4607182418589157889L); + + /** + * Reference: http://www.java-gaming.org/ + * + * @author roquendm + */ + static double sin_roquen_9(double v) { + double i = java.lang.Math.rint(v * ONE_OVER_PI); + double x = v - i * Math.PI; + double qs = 1 - 2 * ((int) i & 1); + double x2 = x * x; + double r; + x = qs * x; + r = s5; + r = r * x2 + s4; + r = r * x2 + s3; + r = r * x2 + s2; + r = r * x2 + s1; + return x * r; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/PoissonSampling.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/PoissonSampling.java new file mode 100644 index 000000000..550d74aaf --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/PoissonSampling.java @@ -0,0 +1,161 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +import java.util.ArrayList; + +import com.jozufozu.flywheel.repack.joml.Random; +import com.jozufozu.flywheel.repack.joml.Vector2f; + +/** + * Generates Poisson samples. + *

+ * The algorithm implemented here is based on Fast Poisson Disk Sampling in Arbitrary + * Dimensions. + * + * @author Kai Burjack + */ +public class PoissonSampling { + + /** + * Generates Poisson samples on a disk. + *

+ * The algorithm implemented here is based on Fast Poisson Disk Sampling in Arbitrary + * Dimensions. + * + * @author Kai Burjack + */ + public static class Disk { + + private final Vector2f[] grid; + private final float diskRadius; + private final float diskRadiusSquared; + private final float minDist; + private final float minDistSquared; + private final float cellSize; + private final int numCells; + private final Random rnd; + private final ArrayList processList; + + /** + * Create a new instance of {@link Disk} which computes poisson-distributed samples on a disk with the given radius diskRadius and notifies the given + * callback for each found sample point. + *

+ * The samples are distributed evenly on the disk with a minimum distance to one another of at least minDist. + * + * @param seed + * the seed to initialize the random number generator with + * @param diskRadius + * the disk radius + * @param minDist + * the minimum distance between any two generated samples + * @param k + * determines how many samples are tested before rejection. Higher values produce better results. Typical values are 20 to 30 + * @param callback + * will be notified about each sample point + */ + public Disk(long seed, float diskRadius, float minDist, int k, Callback2d callback) { + this.diskRadius = diskRadius; + this.diskRadiusSquared = diskRadius * diskRadius; + this.minDist = minDist; + this.minDistSquared = minDist * minDist; + this.rnd = new Random(seed); + this.cellSize = minDist / (float) Math.sqrt(2.0); + this.numCells = (int) ((diskRadius * 2) / cellSize) + 1; + this.grid = new Vector2f[numCells * numCells]; + this.processList = new ArrayList(); + compute(k, callback); + } + + private void compute(int k, Callback2d callback) { + float x, y; + do { + x = rnd.nextFloat() * 2.0f - 1.0f; + y = rnd.nextFloat() * 2.0f - 1.0f; + } while (x * x + y * y > 1.0f); + Vector2f initial = new Vector2f(x, y); + processList.add(initial); + callback.onNewSample(initial.x, initial.y); + insert(initial); + while (!processList.isEmpty()) { + int i = rnd.nextInt(processList.size()); + Vector2f sample = (Vector2f) processList.get(i); + boolean found = false; + search: for (int s = 0; s < k; s++) { + float angle = rnd.nextFloat() * (float) Math.PI2; + float radius = minDist * (rnd.nextFloat() + 1.0f); + x = (float) (radius * Math.sin_roquen_9(angle + Math.PIHalf)); + y = (float) (radius * Math.sin_roquen_9(angle)); + x += sample.x; + y += sample.y; + if (x * x + y * y > diskRadiusSquared) + continue search; + if (!searchNeighbors(x, y)) { + found = true; + callback.onNewSample(x, y); + Vector2f f = new Vector2f(x, y); + processList.add(f); + insert(f); + break; + } + } + if (!found) { + processList.remove(i); + } + } + } + + private boolean searchNeighbors(float px, float py) { + int row = (int) ((py + diskRadius) / cellSize); + int col = (int) ((px + diskRadius) / cellSize); + if (grid[row * numCells + col] != null) + return true; + int minX = Math.max(0, col - 1); + int minY = Math.max(0, row - 1); + int maxX = Math.min(col + 1, numCells - 1); + int maxY = Math.min(row + 1, numCells - 1); + for (int y = minY; y <= maxY; y++) { + for (int x = minX; x <= maxX; x++) { + Vector2f v = grid[y * numCells + x]; + if (v != null) { + float dx = v.x - px; + float dy = v.y - py; + if (dx * dx + dy * dy < minDistSquared) { + return true; + } + } + } + } + return false; + } + + private void insert(Vector2f p) { + int row = (int) ((p.y + diskRadius) / cellSize); + int col = (int) ((p.x + diskRadius) / cellSize); + grid[row * numCells + col] = p; + } + + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/SpiralSampling.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/SpiralSampling.java new file mode 100644 index 000000000..2a1c0a77f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/SpiralSampling.java @@ -0,0 +1,101 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +import com.jozufozu.flywheel.repack.joml.Random; + +/** + * Creates samples on a spiral around a center point. + * + * @author Kai Burjack + */ +public class SpiralSampling { + private final Random rnd; + + /** + * Create a new instance of {@link SpiralSampling} and initialize the random number generator with the given seed. + * + * @param seed + * the seed to initialize the random number generator with + */ + public SpiralSampling(long seed) { + rnd = new Random(seed); + } + + /** + * Create numSamples number of samples on a spiral with maximum radius radius around the center using numRotations number of rotations + * along the spiral, and call the given callback for each sample generated. + *

+ * The generated sample points are distributed with equal angle differences around the spiral, so they concentrate towards the center. + * + * @param radius + * the maximum radius of the spiral + * @param numRotations + * the number of rotations of the spiral + * @param numSamples + * the number of samples to generate + * @param callback + * will be called for each sample generated + */ + public void createEquiAngle(float radius, int numRotations, int numSamples, Callback2d callback) { + for (int sample = 0; sample < numSamples; sample++) { + float angle = 2.0f * (float) Math.PI * (sample * numRotations) / numSamples; + float r = radius * sample / (numSamples - 1); + float x = (float) Math.sin_roquen_9(angle + 0.5f * (float) Math.PI) * r; + float y = (float) Math.sin_roquen_9(angle) * r; + callback.onNewSample(x, y); + } + } + + /** + * Create numSamples number of samples on a spiral with maximum radius radius around the center using numRotations number of rotations + * along the spiral, and call the given callback for each sample generated. + *

+ * The generated sample points are distributed with equal angle differences around the spiral, so they concentrate towards the center. + *

+ * Additionally, the radius of each sample point is jittered by the given jitter factor. + * + * @param radius + * the maximum radius of the spiral + * @param numRotations + * the number of rotations of the spiral + * @param numSamples + * the number of samples to generate + * @param jitter + * the factor by which the radius of each sample point is jittered. Possible values are [0..1] + * @param callback + * will be called for each sample generated + */ + public void createEquiAngle(float radius, int numRotations, int numSamples, float jitter, Callback2d callback) { + float spacing = radius / numRotations; + for (int sample = 0; sample < numSamples; sample++) { + float angle = 2.0f * (float) Math.PI * (sample * numRotations) / numSamples; + float r = radius * sample / (numSamples - 1) + (rnd.nextFloat() * 2.0f - 1.0f) * spacing * jitter; + float x = (float) Math.sin_roquen_9(angle + 0.5f * (float) Math.PI) * r; + float y = (float) Math.sin_roquen_9(angle) * r; + callback.onNewSample(x, y); + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/StratifiedSampling.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/StratifiedSampling.java new file mode 100644 index 000000000..8f57894c3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/StratifiedSampling.java @@ -0,0 +1,93 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +import com.jozufozu.flywheel.repack.joml.Random; + +/** + * Creates samples on a unit quad using an NxN strata grid. + * + * @author Kai Burjack + */ +public class StratifiedSampling { + + private final Random rnd; + + /** + * Create a new instance of {@link StratifiedSampling} and initialize the random number generator with the given + * seed. + * + * @param seed + * the seed to initialize the random number generator with + */ + public StratifiedSampling(long seed) { + this.rnd = new Random(seed); + } + + /** + * Generate n * n random sample positions in the unit square of x, y = [-1..+1]. + *

+ * Each sample within its stratum is distributed randomly. + * + * @param n + * the number of strata in each dimension + * @param callback + * will be called for each generated sample position + */ + public void generateRandom(int n, Callback2d callback) { + for (int y = 0; y < n; y++) { + for (int x = 0; x < n; x++) { + float sampleX = (rnd.nextFloat() / n + (float) x / n) * 2.0f - 1.0f; + float sampleY = (rnd.nextFloat() / n + (float) y / n) * 2.0f - 1.0f; + callback.onNewSample(sampleX, sampleY); + } + } + } + + /** + * Generate n * n random sample positions in the unit square of x, y = [-1..+1]. + *

+ * Each sample within its stratum is confined to be within [-centering/2..1-centering] of its stratum. + * + * @param n + * the number of strata in each dimension + * @param centering + * determines how much the random samples in each stratum are confined to be near the center of the + * stratum. Possible values are [0..1] + * @param callback + * will be called for each generated sample position + */ + public void generateCentered(int n, float centering, Callback2d callback) { + float start = centering * 0.5f; + float end = 1.0f - centering; + for (int y = 0; y < n; y++) { + for (int x = 0; x < n; x++) { + float sampleX = ((start + rnd.nextFloat() * end) / n + (float) x / n) * 2.0f - 1.0f; + float sampleY = ((start + rnd.nextFloat() * end) / n + (float) y / n) * 2.0f - 1.0f; + callback.onNewSample(sampleX, sampleY); + } + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/UniformSampling.java b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/UniformSampling.java new file mode 100644 index 000000000..c6551ecf3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/UniformSampling.java @@ -0,0 +1,121 @@ +/* + * The MIT License + * + * Copyright (c) 2016-2021 JOML + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.jozufozu.flywheel.repack.joml.sampling; + +import com.jozufozu.flywheel.repack.joml.Random; + +/** + * Generates uniform samples. + * + * @author Kai Burjack + */ +public class UniformSampling { + + /** + * Generates uniform samples on a unit disk. + * + * @author Kai Burjack + */ + public static class Disk { + private final Random rnd; + + /** + * Create a new instance of {@link Disk}, initialize the random number generator with the given seed and generate numSamples number of sample + * positions on the unit disk, and call the given callback for each sample generate. + * + * @param seed + * the seed to initialize the random number generator with + * @param numSamples + * the number of samples to generate + * @param callback + * will be called for each sample generated + */ + public Disk(long seed, int numSamples, Callback2d callback) { + this.rnd = new Random(seed); + generate(numSamples, callback); + } + + private void generate(int numSamples, Callback2d callback) { + for (int i = 0; i < numSamples; i++) { + float r = rnd.nextFloat(); + float a = rnd.nextFloat() * 2.0f * (float) Math.PI; + float sqrtR = (float) Math.sqrt(r); + float x = sqrtR * (float) Math.sin_roquen_9(a + 0.5 * Math.PI); + float y = sqrtR * (float) Math.sin_roquen_9(a); + callback.onNewSample(x, y); + } + } + } + + /** + * Generates uniform samples on a unit sphere. + * + * @author Kai Burjack + */ + public static class Sphere { + private final Random rnd; + + /** + * Create a new instance of {@link Sphere}, initialize the random number generator with the given seed and generate numSamples number of sample + * positions on the unit sphere, and call the given callback for each sample generate. + * + * @param seed + * the seed to initialize the random number generator with + * @param numSamples + * the number of samples to generate + * @param callback + * will be called for each sample generated + */ + public Sphere(long seed, int numSamples, Callback3d callback) { + this.rnd = new Random(seed); + generate(numSamples, callback); + } + + /** + * Create numSamples number of samples which are uniformly distributed on a unit sphere, and call the given callback for each sample generated. + *

+ * Reference: http://mathworld.wolfram.com/ + * + * @param numSamples + * the number of samples to generate + * @param callback + * will be called for each sample generated + */ + public void generate(int numSamples, Callback3d callback) { + for (int i = 0; i < numSamples;) { + float x1 = rnd.nextFloat() * 2.0f - 1.0f; + float x2 = rnd.nextFloat() * 2.0f - 1.0f; + if (x1 * x1 + x2 * x2 >= 1.0f) + continue; + float sqrt = (float) Math.sqrt(1.0 - x1 * x1 - x2 * x2); + float x = 2 * x1 * sqrt; + float y = 2 * x2 * sqrt; + float z = 1.0f - 2.0f * (x1 * x1 + x2 * x2); + callback.onNewSample(x, y, z); + i++; + } + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/package.html b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/package.html new file mode 100644 index 000000000..be323f5b9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/repack/joml/sampling/package.html @@ -0,0 +1,8 @@ + + + + + +

Contains classes for generating sampling patterns.

+ + From c268b9cc9bf1af10ae961ea1dba9bbb09fe5c7fe Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 24 Dec 2021 16:15:20 -0800 Subject: [PATCH 22/22] Track VBO and EBO via vanilla --- .../flywheel/backend/gl/buffer/GlBufferType.java | 11 +++++++++++ .../flywheel/mixin/BufferUploaderAccessor.java | 12 ++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBufferType.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBufferType.java index b2371c440..53a0ab9ac 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBufferType.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBufferType.java @@ -8,6 +8,7 @@ import org.lwjgl.opengl.GL40; import org.lwjgl.opengl.GL42; import org.lwjgl.opengl.GL43; +import com.jozufozu.flywheel.mixin.BufferUploaderAccessor; import com.mojang.blaze3d.platform.GlStateManager; public enum GlBufferType { @@ -34,9 +35,19 @@ public enum GlBufferType { public void bind(int buffer) { GlStateManager._glBindBuffer(glEnum, buffer); + + switch (this.glEnum) { + case GL15C.GL_ELEMENT_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastEBO(buffer); + case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastVBO(buffer); + } } public void unbind() { GlStateManager._glBindBuffer(glEnum, 0); + + switch (this.glEnum) { + case GL15C.GL_ELEMENT_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastEBO(0); + case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastVBO(0); + } } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BufferUploaderAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/BufferUploaderAccessor.java index 8ee045d05..64b81bd2b 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/BufferUploaderAccessor.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/BufferUploaderAccessor.java @@ -5,12 +5,20 @@ import org.spongepowered.asm.mixin.gen.Accessor; import com.mojang.blaze3d.vertex.BufferUploader; -import net.minecraft.client.renderer.ShaderInstance; - @Mixin(BufferUploader.class) public interface BufferUploaderAccessor { @Accessor("lastVertexArrayObject") static void flywheel$setLastVAO(int id) { throw new AssertionError(); } + + @Accessor("lastVertexBufferObject") + static void flywheel$setLastVBO(int id) { + throw new AssertionError(); + } + + @Accessor("lastIndexBufferObject") + static void flywheel$setLastEBO(int id) { + throw new AssertionError(); + } }